Babies first flow- a dynamic morning alarm

Hello everyone! Last week I made the stupid decision to switch from ST to HASSIO to NR with all the coding experience of a being who had a moderately personalised myspace in 2004. I've spent the last three days bashing my head against a wall and, as a consequence, am rather proud of finishing my first flow, so I thought I'd share!

It's designed to dynamically pull shifts from the scheduling app my work uses, and wake me up/alert me to new shifts accordingly.

I suspect I have done some things in a hugely inefficient manner, so I welcome any tips or tricks you might have. In the mean time, hey mum, look at what I made!

[{"id":"fbf9170154844576","type":"inject","z":"11e35ac60b19fef7","name":"Daily Schedule Check","props":[{"p":"currentTime","v":"","vt":"date"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"*/10 0-23 * * 1,2,3,4,5,0","once":false,"onceDelay":0.1,"topic":"","x":190,"y":160,"wires":[["a3339ad1af45456b","a723de5a0e5f58fd"]]},{"id":"a3339ad1af45456b","type":"moment","z":"11e35ac60b19fef7","name":"Unix to String","topic":"","input":"currentTime","inputType":"msg","inTz":"Etc/Universal","adjAmount":"10","adjType":"hours","adjDir":"add","format":"","locale":"C","output":"currentTime","outputType":"msg","outTz":"Etc/Universal","x":220,"y":200,"wires":[["27f97dbeda1a4a45"]]},{"id":"27f97dbeda1a4a45","type":"ha-api","z":"11e35ac60b19fef7","name":"Check Schedule","server":"18358764.5105d9","version":1,"debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\t   \"type\": \"execute_script\",\t   \"sequence\": [\t       {\t           \"service\": \"calendar.list_events\",\t           \"data\": {\t               \"start_date_time\": msg.currentTime,\t               \"duration\": \"24:00:00\"\t            },\t           \"target\": {\t               \"entity_id\": [\t                   \"calendar.shift\"\t                ]\t            },\t           \"response_variable\": \"service_result\"\t        },\t       {\t           \"stop\": \"done\",\t           \"response_variable\": \"service_result\"\t        }\t    ]\t}","dataType":"jsonata","responseType":"json","outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"results"}],"x":220,"y":240,"wires":[["d295148c34f518dd"]]},{"id":"e05af93610474375","type":"change","z":"11e35ac60b19fef7","name":"Get Start Time","rules":[{"t":"set","p":"eventStartTime","pt":"msg","to":"payload.response.events[0].start","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":360,"wires":[["ef21b6fd37450b6b","306c7548d9f1831a","951e0556198eb3eb"]]},{"id":"ef21b6fd37450b6b","type":"moment","z":"11e35ac60b19fef7","name":"Wake Up Call","topic":"","input":"eventStartTime","inputType":"msg","inTz":"Etc/Universal","adjAmount":"110","adjType":"minutes","adjDir":"subtract","format":"kk:mm","locale":"C","output":"payload","outputType":"msg","outTz":"Etc/Universal","x":480,"y":300,"wires":[["e54993600fcebced"]]},{"id":"306c7548d9f1831a","type":"moment","z":"11e35ac60b19fef7","name":"Notification","topic":"","input":"eventStartTime","inputType":"msg","inTz":"Etc/Universal","adjAmount":"13","adjType":"hours","adjDir":"subtract","format":"kk:mm","locale":"C","output":"payload","outputType":"msg","outTz":"Etc/Universal","x":470,"y":380,"wires":[["8bbd42c8b9a6f7ba"]]},{"id":"a723de5a0e5f58fd","type":"moment","z":"11e35ac60b19fef7","name":"Current Time","topic":"","input":"","inputType":"msg","inTz":"Etc/Universal","adjAmount":0,"adjType":"days","adjDir":"add","format":"kk:mm","locale":"C","output":"payload","outputType":"msg","outTz":"Etc/Universal","x":470,"y":340,"wires":[["8bbd42c8b9a6f7ba","e54993600fcebced"]]},{"id":"e54993600fcebced","type":"join","z":"11e35ac60b19fef7","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":810,"y":300,"wires":[["23139e3254df8299"]]},{"id":"23139e3254df8299","type":"function","z":"11e35ac60b19fef7","name":"Compare Times","func":"var newmsg = {};\nif (msg.payload[0] == msg.payload[1]) {\n    newmsg.payload = \"True\";\n} else {\n    newmsg.payload = \"False\";\n}\n\nreturn newmsg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":300,"wires":[["80190f1441ee6c12"]]},{"id":"d3844cfabb76b50b","type":"api-call-service","z":"11e35ac60b19fef7","name":"","server":"18358764.5105d9","version":5,"debugenabled":false,"domain":"cover","service":"set_cover_position","areaId":["bedroom"],"deviceId":["cff5f3bb40795fd87da0a61dfd82af07"],"entityId":["cover.roller_blind_cover"],"data":"{\"position\":\"35\"}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":870,"y":240,"wires":[["11e6427da419e570"]]},{"id":"8bbd42c8b9a6f7ba","type":"join","z":"11e35ac60b19fef7","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":810,"y":380,"wires":[["0ddca8143f4bde63"]]},{"id":"0ddca8143f4bde63","type":"function","z":"11e35ac60b19fef7","name":"Compare Times","func":"var newmsg = {};\nif (msg.payload[0] == msg.payload[1]) {\n    newmsg.payload = \"True\";\n} else {\n    newmsg.payload = \"False\";\n}\n\nreturn newmsg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":380,"wires":[["15fe1d675bbb2e4d"]]},{"id":"80190f1441ee6c12","type":"switch","z":"11e35ac60b19fef7","name":"Is it Time?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"True","vt":"str"},{"t":"eq","v":"False","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1130,"y":300,"wires":[["d3844cfabb76b50b"],["63fad3015dfaf963"]]},{"id":"15fe1d675bbb2e4d","type":"switch","z":"11e35ac60b19fef7","name":"Is it Time?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"True","vt":"str"},{"t":"eq","v":"False","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":1130,"y":380,"wires":[["8beb59ad1b42260b"],["63fad3015dfaf963"]]},{"id":"7b4e16e60691ad69","type":"api-call-service","z":"11e35ac60b19fef7","name":"","server":"18358764.5105d9","version":5,"debugenabled":false,"domain":"cover","service":"close_cover","areaId":["bedroom"],"deviceId":["cff5f3bb40795fd87da0a61dfd82af07"],"entityId":["cover.roller_blind_cover"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1390,"y":240,"wires":[[]]},{"id":"11e6427da419e570","type":"ha-wait-until","z":"11e35ac60b19fef7","name":"","server":"18358764.5105d9","version":2,"outputs":1,"entityId":"binary_sensor.motion_sensor_motion","entityIdFilterType":"exact","property":"state","comparator":"is","value":"true","valueType":"bool","timeout":"0","timeoutType":"num","timeoutUnits":"seconds","checkCurrentState":true,"blockInputOverrides":true,"outputProperties":[],"entityLocation":"data","entityLocationType":"none","x":1060,"y":240,"wires":[["3726c3f5ad6bd737"]]},{"id":"3726c3f5ad6bd737","type":"delay","z":"11e35ac60b19fef7","name":"","pauseType":"delay","timeout":"20","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1210,"y":240,"wires":[["7b4e16e60691ad69"]]},{"id":"63fad3015dfaf963","type":"debug","z":"11e35ac60b19fef7","name":"Schedule Checked","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1390,"y":340,"wires":[]},{"id":"7fbcf58ff0997afb","type":"api-call-service","z":"11e35ac60b19fef7","name":"","server":"18358764.5105d9","version":5,"debugenabled":false,"domain":"notify","service":"mobile_app","areaId":[],"deviceId":[],"entityId":[],"data":"{\t   \"message\":msg.payload,\t   \"title\":\"Shift Reminder\"\t}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1070,"y":440,"wires":[[]]},{"id":"951e0556198eb3eb","type":"moment","z":"11e35ac60b19fef7","name":"Notification Body","topic":"","input":"eventStartTime","inputType":"msg","inTz":"Etc/Universal","adjAmount":"10","adjType":"hours","adjDir":"subtract","format":"[Your next shift starts at] H:mm","locale":"C","output":"humanised","outputType":"flow","outTz":"Etc/Universal","x":490,"y":420,"wires":[[]]},{"id":"8beb59ad1b42260b","type":"change","z":"11e35ac60b19fef7","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"humanised","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":840,"y":440,"wires":[["7fbcf58ff0997afb"]]},{"id":"d295148c34f518dd","type":"switch","z":"11e35ac60b19fef7","name":"Is Empty?","property":"payload.response.events","propertyType":"msg","rules":[{"t":"nempty"},{"t":"empty"}],"checkall":"true","repair":false,"outputs":2,"x":240,"y":280,"wires":[["d6329784ed1a6131"],["cd78f33a7baad3f0"]]},{"id":"d6329784ed1a6131","type":"switch","z":"11e35ac60b19fef7","name":"Is Holiday?","property":"payload.response.events[0].summary","propertyType":"msg","rules":[{"t":"nempty"},{"t":"cont","v":"Holiday","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":230,"y":320,"wires":[["e05af93610474375"],["cd78f33a7baad3f0"]]},{"id":"cd78f33a7baad3f0","type":"debug","z":"11e35ac60b19fef7","name":"A Shift Ain't One","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":480,"y":200,"wires":[]},{"id":"b12ff7eb9716379a","type":"comment","z":"11e35ac60b19fef7","name":"I Got 99 Problems But","info":"","x":500,"y":160,"wires":[]},{"id":"18358764.5105d9","type":"server","name":"Home Assistant","addon":true}]
4 Likes

Welcome to the Forums @Jessa!

I can't import your flow, but only because I don't have some of the nodes you use.
This is a very cool idea! and shows what is possible with very little code in Node RED.

Nice work on implementing this in Node RED!

This is a great community! and you will quickly find your self in some awesome brain storming discussions, and LOTS of help. :smiley:

2 Likes

Thank you! It's probably the date/time formatter node- the purple one you can see is absolutely instrumental to my success here haha. It is a wonderfully flexible node if you're doing anything even remotely similar to this, highly rate!

I've used it to:

  • convert timestamp unix into iso for the api to understand,
  • offset the shift start time that the api responds with by 2 hrs for my alarm, and by 14 hrs for the notification,
  • and (by using its formatting feature) write the body of my notification without having to use JS to combine strings with variables.

You're definitely right about node-red allowing non-coders to do really cool things. This was, without a doubt, a step up in complexity for me. But ultimately still manageable! And way more rewarding than a sudoku.

Hi @Jessa,

I don't have Home Assistant either but I took a look at your "Compare times" and "Is it time?" nodes.

You are routing messages depending on whether msg.payload[0] == msg.payload[1].
You could get rid of the function and do this with just the switch node, :


Maybe you would need another change node at the top, if your api-call-service is expecting msg.payload == "True"

ps Does your location use Daylight Saving Time? Have you considered how it will cope when the clocks change?

1 Like

Hahaha can't believe I didn't think of that, I literally used that same logic earlier in the flow to determine if its a public holiday or not. See, that's why we love forums!

My location doesn't use daylight savings time; I have used some time offset (which you might see in my export) because the api responds rather literally as GMT+. For example, 5:30:00+4, instead of 9:30:00. I've sanitised some of the export of location and name related stuff which might import a little goofy.

Hello, can i ask a question?

It's a forum Stephan, dive in!

1 Like

Strangely the comparison broke down when I used the switch node instead of a function node. My guess is it has something to do with the msg. carrying a bunch of additional information that the switch node couldn't reconcile, where as using the function node forced it to only compare the given data.