Morning Routines with Node Red

One of the most powerful tools available to the smart home enthusiast is Node-Red. Node red is a graphical interface that allows for quick, drag-and-drop style programing. You are able to do some very powerful functions with just a few built-in blocks and get up and going very quickly. In this post, I will go over how to easily install Nod-Red into Hass-IO and play around with getting an automated morning routine going so that my Phone’s alarm will activate my Philip Hue bulbs in the morning to simulate a gentle sunrise. In this post, I will just be going over how I got this to work, but this is not a detailed tutorial on how to use Node-Red, there are plenty of tutorials online that can explain it better then me.

Our Node-Red Flow

Installing the Node-Red Plugin

Normally, Node-Red is a piece of software that can be installed standalone, but some very nice people have got a easy to install plugin available for Hass-IO. If you followed my previous tutorials on how to install Hass-IO, this process should go fairly smoothly. On the Left hand menu of HomeAssistant, select your Hass-IO admin page, then at the top select “Add-On Store”.

The Add-On Store for Hass-IO

Scroll down to the Community Add-Ons and look for the Node-Red Add-On. In the Node-Red Add-On page, select install to easily add it to your Home Assistant instance.

Once the installation is done, you will need to edit the config to allow it to boot up the first time. Below is the config that I am using.

{
  "credential_secret": "",
  "dark_mode": true,
  "http_node": {
    "username": "",
    "password": ""
  },
  "http_static": {
    "username": "",
    "password": ""
  },
  "ssl": false,
  "certfile": "fullchain.pem",
  "keyfile": "privkey.pem",
  "require_ssl": false,
  "system_packages": [],
  "npm_packages": [],
  "init_commands": []
}

The two main items to note are the “ssl” and “credential_secret” items. If you only plan on accessing Nod-Red locally, set “ssl” to false. As well, Node-Red requires a secret password to be set, this can be anything, I have removed mine for this post. Hit save, and go back to the top and select Start to boot the Add-On. If you want easy access to the edit page, select the “Show in Sidebar” toggle near the top to show Node-Red on the Home Assistant sidebar.

Reading In Alarm time From Phone

One of the first things we need for this is to get the next Alarm time from my phone, which I use as my alarm clock. I get my alarm time from my phone using a Home Assistant Android App, Ariela (Link). I won’t go into details in this post, but the app automatically sends the alarm time to Home Assistant, and from there, we can read in that value into Node-Red. Unfortunately, this alarm time is received as a string and not a time object. Luckily for us, Node-Red makes it easy to convert to a time that can be used. The first part of this flow is to convert the string time and store it back into Home Assistant as a time object.

Since we are using the Node-Red Plugin for Hass-IO, the two works very easily with little setup. The order of the flow is as follows:

  • Read in the Time as String.
  • Check if its different then last check, continue if different.
  • Parse the String to a Javascript Date Object.
  • Format the Date in an order Home Assistant recognizes.
  • Build a JSON message to send to Home Assistant.
  • Add a 5 second delay.
  • Send the formatted JSON message to Home Assistant.

The code for that flow is below:

[{"id":"a9935f20.fb35d","type":"function","z":"6e46fe93.4f0bf","name":"Parse Time-String","func":"\nif (typeof msg.payload === 'string' && msg.payload.length > 0) {\n    msg.payload = new Date(msg.payload);\n} else {\n    // Not expected value, stop here.\n    return null;\n}\nreturn msg;","outputs":1,"noerr":0,"x":497.5000534057617,"y":91.00000190734863,"wires":[["9a5d0f07.fea61"]]},{"id":"65a8d55b.c07cfc","type":"comment","z":"6e46fe93.4f0bf","name":"Gets the next Alarm time from Phone, parses it, then stores it in a input_datetime entity","info":"","x":422.5,"y":45,"wires":[]},{"id":"3aebcdba.8be832","type":"api-call-service","z":"6e46fe93.4f0bf","name":"Set Next Alarm Time","server":"90d2cbdd.ab81a8","version":1,"service_domain":"input_datetime","service":"set_datetime","entityId":"input_datetime.alarm_datetime","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1104.500129699707,"y":153.99999618530273,"wires":[[]]},{"id":"9a5d0f07.fea61","type":"moment","z":"6e46fe93.4f0bf","name":"Format DateTime","topic":"","input":"payload","inputType":"msg","inTz":"America/Vancouver","adjAmount":0,"adjType":"days","adjDir":"add","format":"YYYY-MM-DD HH:mm:ss","locale":"C","output":"payload","outputType":"msg","outTz":"America/Vancouver","x":728.6002044677734,"y":88.80000114440918,"wires":[["f4712d8f.67222"]]},{"id":"f4712d8f.67222","type":"change","z":"6e46fe93.4f0bf","name":"Format JSON for Service Call","rules":[{"t":"set","p":"tempValue","pt":"flow","to":"payload","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.data.datetime","pt":"msg","to":"tempValue","tot":"flow"},{"t":"delete","p":"tempValue","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":708.8001098632812,"y":158.40000915527344,"wires":[["515c15e1.15736c"]]},{"id":"a8fb573f.59ae28","type":"poll-state","z":"6e46fe93.4f0bf","name":"Get Alarm time from Phone","server":"90d2cbdd.ab81a8","version":1,"updateinterval":"1","updateIntervalUnits":"minutes","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.aaron_s_s10_alarm_sensor","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":115,"y":95.00000190734863,"wires":[["2ddd4f25.23d16"]]},{"id":"2ddd4f25.23d16","type":"rbe","z":"6e46fe93.4f0bf","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":314.60000228881836,"y":90.80000114440918,"wires":[["a9935f20.fb35d"]]},{"id":"515c15e1.15736c","type":"delay","z":"6e46fe93.4f0bf","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":916.6000366210938,"y":155.7999973297119,"wires":[["3aebcdba.8be832"]]},{"id":"90d2cbdd.ab81a8","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

The updated time is stored in a input_datetime field.

Is it Alarm Time?

Now that we have an easy to use time, we need to check that time against the current time to see if the alarm is active.

We poll the saved time once a second, compare it to the current time and if time, trigger our morning routine. The order of the flow is as follows:

  • Read in the alarm time every second.
  • Convert it to Date Object.
  • Get Current time and store into msg object.
  • Is current time Greater or Equal to Alarm time?
  • If so, set morning routine to true.

In my flow, I set a flag I named “Morning Mode”. This was to help modularize the smart home, but that block could be replaced with the entire morning routine.

[{"id":"8d4d325d.d4889","type":"switch","z":"6e46fe93.4f0bf","name":"Trigger Automation if True","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"true","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":825.5000457763672,"y":426.0000009536743,"wires":[["dc32e0d4.fd89"]]},{"id":"18009d4f.c88273","type":"rbe","z":"6e46fe93.4f0bf","name":"Update On Change","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":604.5000152587891,"y":426.0000009536743,"wires":[["8d4d325d.d4889"]]},{"id":"856519c9.39d408","type":"change","z":"6e46fe93.4f0bf","name":"Alarm is true","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload","pt":"msg","to":"true","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400.50001525878906,"y":410.0000009536743,"wires":[["18009d4f.c88273"]]},{"id":"81530b5.40713f8","type":"switch","z":"6e46fe93.4f0bf","name":"Is current time > Alarm Time?","property":"timestamp","propertyType":"msg","rules":[{"t":"gte","v":"payload","vt":"msg"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":161.50000762939453,"y":410.0000009536743,"wires":[["856519c9.39d408"],["7bc9e659.1d00f8"]]},{"id":"cbeb73e8.b795c","type":"poll-state","z":"6e46fe93.4f0bf","name":"Poll Next Alarm Time","server":"90d2cbdd.ab81a8","version":1,"updateinterval":"1","updateIntervalUnits":"seconds","outputinitially":false,"outputonchanged":false,"entity_id":"sensor.aaron_s_s10_alarm_sensor","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":104.00000762939453,"y":326,"wires":[["81416285.5eb2a"]]},{"id":"81416285.5eb2a","type":"function","z":"6e46fe93.4f0bf","name":"Covert Time to TimeStamp","func":"\nif (typeof msg.payload === 'string' && msg.payload.length > 0) {\n    // Parse the time from the time input to Date object.\n    AlarmTime = new Date(msg.payload);\n    \n    // Subtract 10 minutes from the set time. Automation takes 10 minutes to complete\n    //AlarmTime.setMinutes(AlarmTime.getMinutes() - 10);\n    \n    // Convert the time to timestamp.\n    msg.payload = AlarmTime.getTime();\n} else {\n    // Not expected value, stop here.\n    return null;\n}\nreturn msg;","outputs":1,"noerr":0,"x":358.00001525878906,"y":327,"wires":[["fdd81cb.258bce"]]},{"id":"fdd81cb.258bce","type":"change","z":"6e46fe93.4f0bf","name":"Get Current time","rules":[{"t":"set","p":"timestamp","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":612.0000152587891,"y":327.8000240325928,"wires":[["81530b5.40713f8"]]},{"id":"7bc9e659.1d00f8","type":"change","z":"6e46fe93.4f0bf","name":"Alarm is false","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload","pt":"msg","to":"false","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":404.0000114440918,"y":460.00000190734863,"wires":[["18009d4f.c88273"]]},{"id":"6c18eb08.005d34","type":"inject","z":"6e46fe93.4f0bf","name":"Test Automation (Alarm Time is now)","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":242.60000610351562,"y":510.00000190734863,"wires":[["fdd81cb.258bce"]]},{"id":"25207a0f.784f66","type":"inject","z":"6e46fe93.4f0bf","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":151.60001373291016,"y":477.00000381469727,"wires":[["7bc9e659.1d00f8"]]},{"id":"4192e783.57eb88","type":"comment","z":"6e46fe93.4f0bf","name":"Compare the current time with Alarm time and trigger Wakeup if its alarm time","info":"","x":410.60001373291016,"y":285.80002307891846,"wires":[]},{"id":"f82bd516.a53ef8","type":"comment","z":"6e46fe93.4f0bf","name":"Current time stored in msg.timestamp","info":"","x":865.6000595092773,"y":327.8000240325928,"wires":[]},{"id":"dc32e0d4.fd89","type":"api-call-service","z":"6e46fe93.4f0bf","name":"Turn On Morning Mode","server":"90d2cbdd.ab81a8","version":1,"service_domain":"homeassistant","service":"turn_on","entityId":"input_boolean.morning_mode","data":"{   \"entity_id\": \"input_boolean.morning_mode\" }","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1085.5,"y":424,"wires":[[]]},{"id":"90d2cbdd.ab81a8","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

Morning Routine

My morning routine is fairly simple now, just turning my bedroom lights on. The lights start at 1% brightness and fade brighter to 50% over 10 minutes. All of that logic is handled by Home Assistant; We just need to tell it what we want.

[{"id":"1036f4b0.90ae3b","type":"api-call-service","z":"e71d308f.d8ee3","name":"Set Bedroom to Warm/1% Brightness","server":"90d2cbdd.ab81a8","version":1,"service_domain":"light","service":"turn_on","entityId":"light.bedroom","data":"{\"entity_id\":\"light.bedroom\",\"brightness_pct\":1,\"color_temp\":454}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":500.50001525878906,"y":92.00000190734863,"wires":[["571ba54.651d25c"]]},{"id":"571ba54.651d25c","type":"api-call-service","z":"e71d308f.d8ee3","name":"Fade On Bedroom to 100% over 10 minutes","server":"90d2cbdd.ab81a8","version":1,"service_domain":"light","service":"turn_on","entityId":"light.bedroom","data":"{\"entity_id\":\"light.bedroom\",\"transition\":600,\"brightness_pct\":50}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":521.9999847412109,"y":135.00000190734863,"wires":[[]]},{"id":"acdf1173.83cb4","type":"server-state-changed","z":"e71d308f.d8ee3","name":"Is it Morning Mode?","server":"90d2cbdd.ab81a8","version":1,"entityidfilter":"input_boolean.morning_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":174,"y":97,"wires":[["1036f4b0.90ae3b"],[]]},{"id":"90d2cbdd.ab81a8","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

Testing our Node-Red Flow

Now that we have all of our pieces in place, we want to deploy and test to see if it all works. Select the red “Deploy” button to save and run our flow. To test, we can set our alarm time to a few minutes ahead from current time, and see if it all runs as expected.

Future Work

The current flow is pretty simple, just turning on a light. This can easily be expanded to activate other lights or functions when the alarm turns on, or even a delayed amount of time afterwards. The one downside to the current setup is that the “morning mode” flag gets set to true, but never set to false, which means that if not reset, the next alarm time the lights won’t turn on. That can be solved in several ways, and I will explore that in the future.

If you want to see future posts such as this, please consider subscribing to my blog so that you can catch all posts as soon as they are posted, Thanks!