Node Red 

Table of Contents

Nodes in use

node-red

node-red-contrib-arp

node-red-contrib-hold

node-red-contrib-wemo-emulator

node-red-dashboard

node-red-node-pi-gpio

node-red-node-email

node-red-contrib-image-output

node-red-contrib-tfjs-coco-ssd

Node Red PIRcam Flow

Initialisation

On boot up the Inject node automatically runs to set a default value for the dashboard Numeric (Refresh Interval) and activate a Trigger node for the ARP node. A msg.payload  of off  is delivered to dashboard Buttons (PIR alerts and RECORD) to set their status (a delay in delivery was found to be beneficial).

Last Seen

The dashboard Text node (Face) uses a face icon to display the presence of household mobiles and a text timestamp of the last motion activity detected.  The RPI-GPIO node (PIN: 7) has a debounce of 25ms, if its msg.payload going to the Switch  node has a value of != 0 then it triggers the Change node to set the msg.payload $now i.e. current date and time, and msg.colour to red. After 2 seconds the Trigger node reverts msg.colour to black.  

The datestamp in the payload  is formatted using the AngularJS date format string in the Value format of the Text node to display day of Week, date in month and time.

<font color = {{colour}}>{{payload| date: 'EEEE,'}}<br>{{payload| date: 'MMM d,'}} <br>{{payload| date: 'h:mm a'}}</font>

The font color  as set by msg.colour is distinct from the msg.color used for the face icon. 

The face icon is displayed by the Text node's Label 

<br><br><font color= {{color}} ><span class="material-icons">face</span></font>

 the color of which is set by msg.color in the Function node (Away/Home) depending on whether any of the mobile phones are connected to the local network, as ascertained by the ARP node. The payload from the Function node is set to either on or off which determines what happens in PIR alerts, but needs to be deleted before passing the msg.color on to Text node (Face) icon so that datestamp in the Face node is not overwritten. A Filter node limits the flow to changes in the payload message.

Function node (Away/Home)

The frequency that the Address Resolution Protocol is accessed is set at initialisation. 

PIR alerts & Gate Controls

The Dashboard Switch (PIR alerts) enables PIR sensor alerts via Email, and provides visual indication of the status by changing the icon as well as its colour. Additionally, when set to "on" it activates the Record, piCam alert and piCam Switches.  If set to "off" then piCam alert and piCam are switched off. PIR alerts Switch can be controlled by Alexa and is automatically switched to "on" whenever all the household mobile phones are off the premises. This Alarm system is automatically disabled when at least one mobile returns, however there is around a 2 min delay for its presence to be detected. If the Alexa Audio Alert is "on" then the Alarm system is disarmed immediately following the returning home announcement, by an "off" message sent to PIR alerts via the Link in node.

PIR alerts has the label {{"<font color=grey><b>PIR alerts</b></font>"}} and has icons notifications_active and notifications_off set to colour red and grey respectively. Pass through msg is also enabled so that the Wemo-emulator payload message of "on" or "off" can control "PIR alerts which has the same payload strings for on and off respectively, as required for pass through msg. Alexa has voice activated routines to control the Wemo-emulator. The ARP node has the MAC addresses of the mobile phones associated with the household so that whenever all the mobiles are are no longer on the local network the PIR alerts Switch automatically receives the msg.payloads of  "on" or "off" from the Switch node (switch on/off) and if on then an automatic notification is sent via the Link out node to Mailer.  Any change in the status of PIR Alerts results in updating of the Change node (set flow.PIRstatus) and the Function node (Gate: control).

Whenever the lastSeen DATE is updated (msg.payload SET $now()) the flow continues through Link in nodes to towards the Link out node (to Mailer). Whether or not an email motion sensor alert is sent is dependent on the Function node (Gate control)  and the Dashboard Switch (PIR alerts). Messages arriving at the Function node from lastSeen DATE do not have a message topic, whereas those from PIR alerts have msg.topic set to "gate". If "Gate control" gets a messages with the msg.topic "gate" and a msg.payload of "on" from dashboard Switch node (PIR Alerts) then the gate is set to open to allow lastSeen Date messages to pass through to the Link out node to Mailer, otherwise its message is dropped.



The lastSeen DATE Change node's msg.payload of  $now()  is changed in the PIR Alert Change node with a Jsonata xpression so that the date and time are appended '☎ PIR alert HALL.... ' & $moment(payload).format("MMM-DD HH:mm") before being forwarded via the Link out node to Emailer.

An "on" msg.payload from the Switch node (switch on/off) originating from ARP passes to the Switch node (Gate: PIR off) and if flow.PIRstatus is set to "off" then msg.payload is allowed pass through and in so doing prompts a notification email of "HA PIR on". The msg.payload is also forwarded via the Link out node to Alexa Audio Alerts.

Record and Post to Sheets

This section provides the user with two Dashboard controls. 

     The RECORD switch is also set to "on" status whenever the PIR alerts Switch is changed to "on" status.

2. The Refresh Interval (minutes) Numeric control which sets an interval of 1 to 10 minutes between the registering of PIR movement activity in order to reduce multiple notifications.

Dashboard Record, Table Chart of Recent Activity and Refresh Interval control.

Record, Display and Post to Sheets

iFrame embedded Google Timeline charts stopped working!
May 2023

Any change in the on/off status of the dashboard Switch (RECORD) determines how the Switch node directs the Template nodes to either hide or show the dashboard Table.

"on" : {"group":{"show":["HA_PIR_Table"]}

or "off": {"group":{"hide":["HA_PIR_Table"]}}

Switching the RECORD status also updates the Change node (set flow.Rstatus), and the condition of the "flow.Rstatus" variable is used in the Switch node (Gate: Record if ON (i.e. Rstatus 'on')) to determine whether the msg.payload containing  date and time from lastSeen Date via the Link in node should pass through. 

The payload value from Dashboard Numeric node (Refresh Interval (minutes)) sets the "flow.refresh" variable in the Change node (set flow.refresh (minutes)) whenever it is changed. The variable is used in the Function node (Refresh Rate Limiter) to limit the number recorded PIR movement instances coming through the Gate: Record if ON.

// Discard Multi Messages - set by Refresh Interval

var interval = (flow.get('refresh')) // minimum interval between messages (ms)

context.lastTime = context.lastTime || 0;

var now = Date.now();

if (now-context.lastTime > interval) {

  context.lastTime = now;

  return msg;

} else {

  return null;

}

The Trigger node (momentary request) sends a message payload of "1" followed by an empty payload two seconds later to the GET Http request node so that a Movement of 1 and nothing are recorded in Sheets in close succession.

A Status node (status PIR alerts) is used turn on the Record Switch whenever the Switch (PIR alerts) is changed to "on" status. 

ALEXA Audio Alerts

The Inject  node (Default Alert) sets an initial editable default message in the dashboard Audio Alert on startup. Whenever there is a PIR alert it is forwarded on to the ALEXA Audio Alerts group via the Link in node. Depending on whether the flow.LOCstatus is "away" or "home" the msg payload is set to flow.Audioalert (as set by the dashboard Audio Alert) or "Welcome Home" before being directed towards the MQTT node (Shout) on the raspberry pi paired with the Amazon Echo Dot bluetooth speaker. If the latter, the msg.payload is then set to "off" and forwarded to the PIR alert switch thereby stopping any further alerts. Whether the msg.payload gets through the Gate Function node  depends on the ui Switch which can set the gate to be open or closed.

ui Switch 

colour set by ui Template

<style>

/* Background colour of entire widget */

md-card.nr-dashboard-switch.Alexaudio {

background-color: #0094ce;

}

Function node


if (msg.topic === "gate") {

    context.pass = (msg.payload === true) ? true : false;

    return null// exit out early as it's just the control

}

if (context.pass) {

    return msg; // if enabled pass msg

}

return null; // or drop it

On entering or exiting the geofenced area, the principal mobile with the IFTTT sends an email containing the text "entered" or "exited" to the email account associated with the Node Red Flow. This is received by the IFTTT location  Email in node,  and as determined by the Switch  results in a confirmatory location status email back to the principal mobile. The location status of "flow.LOCstatus" is set to "home" or "away" as appropriate in the Change nodes and msg.payloads set accordingly for an email notification via the Link out node to Emailer.  If "home", then the payload is modified in the Change node set welcome msg  and stored in the Hold node (hold Welcome msg). As payload Welcome Change node will have set the "flow.LOCstatus" to "home" the next PIR alert is directed to the Change node trigger welcome msg reset LOCstatus to away so that the stored welcome message is sent to Shout and Function node PIR to off sends a msg.payload of "off" to PIR alerts via the Link out node.


Audio Alert

As the pi 2B running HA PIRcam Node Red Flow does not have bluetooth, the Audio Alert Flow is on a pi that does. The Flow is on a raspberry pi Zero 2W paired with the Amazon Echo Dot bluetooth speaker and is accessed from the main HA PIRcam Flow via an MQTT node (Shout).

Google Translate is used to convert text to sound which is output as an mp3 which is converted by ffmpeg on the fly to a wav piped to aplay. When done on the fly the spaces in the url get request must be replaced by %20 in the msg.payload within the TTS function, and then enclosed in quotes. 


TTS function node


msg.payload = encodeURIComponent(msg.payload.trim());// replace spaces with %20 for url GET REQUEST

msg.url ="https://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&tl=en&q="+msg.payload;

msg.payload = "'"+msg.url+"'";// need enclose REQUEST in quotes

return msg;

piCam Alert

NB Webcontrol must have been enabled on MotionEYE as described in Camera section

The piCam's Motion Detection mirrors the pirAlert status and so is automatically switched on when all mobiles are away from home. The Filter only allows pirAlert status.text to pass in the event of a change in the status "on" or "off" to trigger the piCam Switch automatically. The piCam Switch can also be set manually in the dashboard. A URL instruction is sent to the piCam via the HTTP GET Request to Start or Pause Motion Detection in MotionEYE and a UTF-8 string is returned indicating completion of the instruction, this is used in the Function node (Color Mapping) to set the msg.color of the Switch (piCam Alert) dashboard text. Green indicates that MotionEYE has responded to the instruction and Motion Detection is active.


The Switch (piCam Alert) sets flow.piCam to on or off, in the Change node (set flow.piCam). The Function Permit  

determines whether a email notification is sent when movement is detected by MotionEYE and a notification is received in Node Red via the Http In Webhook (set to POST and url /webhook). So as not to be inundated with messages the Delay (Alert Rate Limiter) is set to 1 message per 30 seconds and all intermediate messages dropped. That a notification has been sent from MotionEYE is also indicated in the dashboard by forwarding the msg.payload from the Change node (PiCam Motion Detected) to the Color Mapping so that piCam Alert's text is set to red, the default colour. As the msg.payload is forwarded before processing in Permit then the indication is independent of setting the piCam Alert dashboard Switch to "on" for receiving email notifications. (Red piCam Alert text can also indicate that the camera is malfunctioning.)

The Inject Nodes RESTART, QUIT and END are for the webcontrol interface of MOTION.

A Call to a Web Hook can be set in MotionEYE either in the Motion Notifications or the File Storage settings.

The latter has the advantage that the msg.obj received by Node Red includes the location of the image and/or video file recorded when a motion is detected by the camera. This could be used to associate an image with a email (maybe try implementing this at a later date).

The Web Hook call in MotionEYE  running on the Raspberry pi with the camera is directed to the Node Red PIR Raspberry pi's URL /webhook.


The Node Red HTTP in (Webhook) is set to Post and accept file uploads. In response to the MotionEYE Web Hook Call it triggers the Change node to set an email alert for the Link out node to Emailer. The flow is limited by the Alert Rate Limiter  which discards multi messages. The Function node  only allows any message to pass if the piCam alert switch is on, ie flow.piCam is on, otherwise it is discarded.


Accessing Motion Images & Videos

When alerted

For Camera 1 the playback addresses are :

For  pictures
http://192.168.n.n:8765/picture/1/preview/plus_unique_date/and_time.mpg

For Static image of initial movement in Video
http://192.168.n.n:8765/movie/1/preview/plus_unique_date/and_time.mp4

For Video
http://192.168.n.n:8765/movie/1/playback/plus_unique_date/and_time.mp4

The File Storage call to a Web Hook can be used for finding the unique name of the image or video, so that it can be appended to the playback http address. This can be done using JSONATA in a Change node

  
e.g. For pictures the msg.payload from Http request, an Http In  (set to POST and url /webhookfile) is set to the value in a Change node
  $replace(req._parsedUrl.query,"/var/lib/motioneye/Camera1","http://192.168.n.n:8765/picture/1/preview")

and for the Static image of initial movement

$replace(req._parsedUrl.query,"/var/lib/motioneye/Camera1","http://192.168.n.n:8765/movie/1/preview")


The Change node (extract URL) sets msg.url as described above for e.g. the Static image of initial movement. An Http Request (set to: GET, and payload as Send as request body, with Return as a binary buffer) is processed by Base64 (Action : Encode as Base64) and output in a ui Template (set to: Reload last value on refresh - so that the image persists in the dashboard on refresh). Using the following Template 

the image fills the desired grid pattern set by Size.

While this technique is useful for accessing individual images and videos through the Web Control Interface as and when notified by the Webhook it does not lend itself to accessing prior recordings but can be useful for providing TensorFlow with the motion file image for recognition analysis.


SSH into your Pi runing MotionEYE WITHOUT a Password with SSH Keys

In rpi 1 terminal hosting Node Red type and enter

ssh-keygen

Accept default file in which to save key

then asked whether you want a pass phrase so ENTER and again for verification.

Copy over the RSA key to the other rpi 2 runing MotionEYE by entering into the terminal

ssh-copy-id pi@[ip address]

you will asked for the password for the rpi 2, so authenticate with password for rpi 2

Then it is possible to SSH into rpi2 from rpi 1 without having to enter a password.


SSH access will allow full access of files on rpi2 from rpi.

Image Activity

Input needed for hiding Table/Image

An Image  is only displayed in the dashboard if there has been a piCam Alert. The alert is forwarded to the Image Activity section from the piCam Alert section through changes in Color Mapping msg.color. The filter node  blocks the flow of msg.color unless value has changed. The Function (ImgShow?) sets a context variable to True or False  flow.set("ImgShow",(msg.color==="red")?true:false);  so that the switch node can make different templates for the ui control depending on whether there was a piCam alert.

For TRUE

{"group":{"hide":["HA_PIR_Table"],"show":["HA_PIR_Bits"],"focus":true}}

For FALSE

{"group":{"show":["HA_PIR_Table"],"hide":["HA_PIR_Bits"]}}

The reason for using a context variable flow.ImgShow is that whenever a device opens the dashboard it must determine the template status to be applied to the final ui control. The Connect ui control (outputs on Connect, lost, change tab or group events) acts as a trigger and ensures that the correct template is applied.

The ui Buttons View Image and View Table  appear in Table and Image groups respectively and act as toggle buttons to display the groups through the Function node Table or Image.

flow.set("ImgShow", (flow.get("ImgShow")==false) ? true : false);

msg.payload = flow.get("ImgShow")

return msg;

ssh Find Motion mp4

While the Webhook in the piCam Alert section alerts when movement has been detected in MotionEYE, the Webhook Img notifes whenever a Motion File has been saved by MotionEYE, and causes the Exec node to run a bash script 

ssh pi@192.168.n.n 'find  /var/lib/motioneye/Camera1/*/  -type f -name "*.mp4"' | sort -r

which finds all the MotionEYE mp4 files on the Pi running MotionEYE and orders them with the most recent first in a msg.payload string. The Function node (Motion Files Array Collation)  fragments the msg.payload string into an array consisting of just the date and time with the mp4 extension.

const myarray = msg.payload.split('/var/lib/motioneye/Camera1/');

msg.payload = myarray.slice(1);

return msg;

The array is the Split and the individual elements are prepended with the Webview information for the Static image of initial movement in Video

msg.payload = "http://192.168.n.n:8765/movie/1/preview/"+msg.payload;

return msg;

before rejoining the elements back into an array and saving the msg.payload array as a flow.MotionFiles.

Swipe through Motion Files

The Webhook Img also triggers with a suitable delay the ui Button Refresh Motion Files array whenever a new Motion event is saved to flow.MotionFiles, and along with a "change event" in the ui Control resets the index for the array in the Change node set msg.i.

Having reset the the MotionFile Index with msg.i the Change node (url MotionFile;Set Count) adds to the message output the flow.MotionFile as the msg.payload and determines the size of the array and added as msg.Count, and sets the msg.url to the Index value i of the msg.payload.

The Change node (Add to msg: Flow Vid and Datestamp)  manipulates MotionFiles' element in the  msg.url to create a playback video url msg.vid. The datestamp is also extracted from the url and  formatted. msg.Count and msg.Index are also created for use in the ui Text.

url MotionFile; Set Count 

Add to msg: Flow Vid and Datestamp

MotionFile Index

ui Template

<style>

    img {

        object-fit: contain;

    }

    .video{

        object-fit: contain;

        border:3px solid red;

    }

</style>


<script>

    // include controls for video on iPhone

    document.getElementById("myVideo").controls = (navigator.platform == "iPhone") ? true : false;

</script>


<img ng-src = "{{msg.url}}"

ng-swipe-right = "send({i:msg.i+1})"

ng-swipe-left = "send({i:msg.i-1})"

ng-hide = "showToggle"

ng-click = "send({i:msg.i});showToggle=!showToggle"

/>


<span class="video" ng-dblclick = "showToggle=!showToggle;send({i:msg.i})">

    <video id ="myVideo" ng-src="{{msg.vid}}" autoplay width="100%" height="100%" playsinline poster >

    </video>

</span>

The ui Template displays the Static Image (msg.url) or Video (msg.vid) which can be swiped through using Angular directives. The Change node (MotionFile Index) ensures that the upper and lower limit of the array is not exceeded when swiping.

msg.i = i>=$flowContext("Count")?$flowContext("Count")-1:i

msg.i = i<=0?0:i


Alert Limit


Since the alert is dependent on the MQTT connection then a warning in the event of  disconnection needs to be displayed on dashboard. In the previous § Image Activity, the camera icon in View Image displays red background when MQTT server is disconnected, yellow when reconnecting and white when connected. 

Any motion detected by MotionEYE results in an alert notification arriving from § piCam Alert  node group which is then held in the hold node. The alert is only released from there if its associated image file contains a person as determined by Tensorflow. Any alert that hasn't been released gets overwritten. In this way false alerts due e.g. to changes in lighting are avoided, though the 'false' motion files are still retained. 

However, if the MQTT server links are broken then alerts are not limited to those associated with an image file containing a person instead all alerts pass through to the § Mailer., as determined by the MQTT status and Function (Gate) nodes.

The URL of the Webhook Img sent from the § Image activity  group of nodes is determined and assigned to msg.url in the Switch Node (Extract URL) by $replace(req._parsedUrl.query,"/var/lib/motioneye/Camera1","http://192.168.n.n:8765/movie/1/preview") for the http request node to GET as a binary buffer and to send as the image binary to the mqqt out node for processing by the TFSJ Restrict PiCam Alert to Person Class node group.

The MQTT Status and Gate Function nodes

The Status node monitors MQTT node (sensors/kitchen/camera) and in the Change node  

so that the Boolean payload is only TRUE if the status.fill of the MQTT node is green, i.e. connected. 

Gate All Function

function setStatus() {

 if ((context.get('gate')===true)) {

 node.status({fill:"red", shape:"dot", text:'closed'});

 } else {

 node.status({fill:"green", shape:"dot", text:'open'});

 }

}


if (context.get('gate') !== true ) {

    context.set('gate', false);

    setStatus();

}


if (msg.topic == "gate") {

    context.set('gate',msg.payload);

    setStatus();

    return;

}


if (context.get('gate') === false ) {

    return msg;

}

return;

Gate Limit Function

function setStatus() {

 if (!(context.get('gate')===true)) {

 node.status({fill:"red", shape:"dot", text:'closed'});

 } else {

 node.status({fill:"green", shape:"dot", text:'open'});

 }

}


if (context.get('gate') !== false ) {

    context.set('gate', true);

    setStatus();

}


if (msg.topic == "gate") {

    context.set('gate',msg.payload);

    setStatus();

    return;

}


if (context.get('gate') === true ) {

    return msg;

}

return;

Here the image is analysed by the tf coco ssd node. If the node returns a msg.class.person then the switch node passes it on to the set msg.payload node which sets msg.payload to true. The message is returned to the Alert Restrict node group via the mqtt nodes (sensor/kitchen/person). In the Switch node msg.trigger is set to the returned msg.payload value of True, which releases the alert from the hold node. The alert is sent to mailer. 

Mailer 

Alerts/Notifications are best sent to the mobile device using an email account used only for the Node Red Account. The Change node sets msg.topic to the desired email header while the message comes from the Link in node.