Help with NodeRed Garage Door Open Reminder Flow?

Hi All -

I have a flow I’m hoping for a little input on.

I’m trying to design an automation for a Garage Door Notification flow. In words my goal is this:

After sunset, notify me if the garage door is left open for more than 15 minutes. Send an actionable notification to my phone with an option to close the door immediately OR ignore for 30 minutes. If ignored, it should then repeat. If the door is closed before the 15 minutes is up, then no notifications should be sent (i.e. the flow should terminate). If the door is up before sunset, then the notifications should start 15 minutes after sunset “starts” (if that makes sense, lol).

So last night my flow seemed to be working. But today I came home at about 4pm (daylight). 15 minutes after opening my garage door and leaving it open, I received a notification. This is not the behavior I was expecting as it was not after sunset yet.

I’m using a bigtimer node set for sunset-sunrise and I’m figuring this is where the problem is, but not sure why. I have a boolean on/off for “Nighttime?” set by Bigtimer as part of another thread - I am not sure I could use that in the automation though because it changes only twice a day (at sunset and sunrise).

I’m probably missing something dumb, but here’s my flow:

Here’s the JSON (lat/long removed for obvious reasons):

[{"id":"919173f3b6568e0f","type":"bigtimer","z":"ff9725e964a840cf","outtopic":"","outpayload1":"on","outpayload2":"off","name":"Sunset-Sunrise","comment":"","lat":"","lon":"","starttime":"5004","endtime":"5003","starttime2":0,"endtime2":0,"startoff":"0","endoff":"0","startoff2":0,"endoff2":0,"offs":0,"outtext1":"","outtext2":"","timeout":1440,"sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"day6":0,"month6":0,"day7":0,"month7":0,"day8":0,"month8":0,"day9":0,"month9":0,"day10":0,"month10":0,"day11":0,"month11":0,"day12":0,"month12":0,"d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"d6":0,"w6":0,"xday1":0,"xmonth1":0,"xday2":0,"xmonth2":0,"xday3":0,"xmonth3":0,"xday4":0,"xmonth4":0,"xday5":0,"xmonth5":0,"xday6":0,"xmonth6":0,"xday7":0,"xmonth7":0,"xday8":0,"xmonth8":0,"xday9":0,"xmonth9":0,"xday10":0,"xmonth10":0,"xday11":0,"xmonth11":0,"xday12":0,"xmonth12":0,"xd1":0,"xw1":0,"xd2":0,"xw2":0,"xd3":0,"xw3":0,"xd4":0,"xw4":0,"xd5":0,"xw5":0,"xd6":0,"xw6":0,"suspend":false,"random":false,"randon1":false,"randoff1":false,"randon2":false,"randoff2":false,"repeat":true,"atstart":true,"odd":false,"even":false,"x":100,"y":920,"wires":[["479e4a5089231ba3"],[],[]]},{"id":"479e4a5089231ba3","type":"api-current-state","z":"ff9725e964a840cf","name":"Door 1 open?","server":"ab971ec0.4114f","version":3,"outputs":2,"halt_if":"Violated","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.elkm1_garage_oh_door_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":300,"y":900,"wires":[["3151044b14af23e0"],["422212bdb1496dc3"]]},{"id":"422212bdb1496dc3","type":"change","z":"ff9725e964a840cf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"off","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":960,"wires":[["3151044b14af23e0"]]},{"id":"3151044b14af23e0","type":"trigger","z":"ff9725e964a840cf","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"15","extend":false,"overrideDelay":false,"units":"min","reset":"off","bytopic":"all","topic":"topic","outputs":1,"x":490,"y":900,"wires":[["300accb2038d56cb"]]},{"id":"300accb2038d56cb","type":"change","z":"ff9725e964a840cf","name":"Set Message","rules":[{"t":"set","p":"message","pt":"msg","to":"Garage Door 1 is open after sunset","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":900,"wires":[["0da165589bdba32c"]]},{"id":"0da165589bdba32c","type":"change","z":"ff9725e964a840cf","name":"Set Actions","rules":[{"t":"set","p":"action1","pt":"msg","to":"CLOSEDOOR1","tot":"str"},{"t":"set","p":"actionTitle1","pt":"msg","to":"Close Door","tot":"str"},{"t":"set","p":"action1x","pt":"msg","to":"IGNOREDOOR1","tot":"str"},{"t":"set","p":"actionTitle1x","pt":"msg","to":"Ignore x 30 mins","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":900,"wires":[["6dd3b9b7ad778128"]]},{"id":"ab971ec0.4114f","type":"server","name":"Home Assistant","addon":true}]

Thanks in advance for the help!

This may be a simpler approach. Note: I use:

Actionable Notifications Subflow for Android | node-red-contrib-home-assistant-websocket

[{"id":"6dc0247c.d7210c","type":"subflow","name":"Actionable Notification","info":"[Documentation](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/cookbook/actionable-notifications-subflow-for-android.html)\n","category":"","in":[{"x":84,"y":80,"wires":[{"id":"9d85d137.fe487"}]}],"out":[{"x":1172,"y":128,"wires":[{"id":"974bd48d.c253e8","port":0}]},{"x":1172,"y":176,"wires":[{"id":"974bd48d.c253e8","port":1}]},{"x":1172,"y":224,"wires":[{"id":"974bd48d.c253e8","port":2}]},{"x":964,"y":240,"wires":[{"id":"5bc7345c.07b1cc","port":1}]}],"env":[{"name":"service","type":"str","value":"","ui":{"label":{"en-US":"Notify Service"},"type":"input","opts":{"types":["str"]}}},{"name":"title","type":"str","value":"","ui":{"label":{"en-US":"Title"},"type":"input","opts":{"types":["str"]}}},{"name":"message","type":"str","value":"","ui":{"label":{"en-US":"Message"},"type":"input","opts":{"types":["str"]}}},{"name":"action1Title","type":"str","value":"","ui":{"label":{"en-US":"Action 1 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action1Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 1 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"action2Title","type":"str","value":"","ui":{"label":{"en-US":"Action 2 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action2Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 2 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"action3Title","type":"str","value":"","ui":{"label":{"en-US":"Action 3 Title"},"type":"input","opts":{"types":["str"]}}},{"name":"action3Uri","type":"str","value":"","ui":{"label":{"en-US":"Action 3 URI (optional)"},"type":"input","opts":{"types":["str"]}}},{"name":"userInfo","type":"bool","value":"false","ui":{"label":{"en-US":"Populate User Information"},"type":"checkbox"}},{"name":"sticky","type":"bool","value":"false","ui":{"label":{"en-US":"Sticky"},"type":"checkbox"}},{"name":"group","type":"str","value":"None","ui":{"label":{"en-US":"Group"},"type":"select","opts":{"opts":[{"l":{"en-US":"None"},"v":""},{"l":{"en-US":"Cameras"},"v":"camera"},{"l":{"en-US":"Security"},"v":"security"},{"l":{"en-US":"Garage"},"v":"garage"},{"l":{"en-US":"Laundry Room"},"v":"laundry_room"}]}}},{"name":"color","type":"str","value":"","ui":{"label":{"en-US":"Color"},"type":"input","opts":{"types":["str"]}}},{"name":"timeout","type":"num","value":"","ui":{"label":{"en-US":"Timeout"},"type":"input","opts":{"types":["num"]}}},{"name":"icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","outputLabels":["Action 1","Action 2","Action 3","Cleared"],"status":{"x":244,"y":272,"wires":[{"id":"204dbcfc.144ae4","port":0}]}},{"id":"f9e57204.71076","type":"function","z":"6dc0247c.d7210c","name":"create service call","func":"const actions = [];\n[1,2,3].forEach(i => {\n    const name = `action${i}`\n    const id = flow.get(`${name}Id`);\n    const title = env.get(`${name}Title`);\n    const uri = env.get(`${name}Uri`);\n    const action = !!uri.length ? 'URI' : title ? flow.get(`${name}Id`) : undefined;\n    \n    actions.push({\n        action,\n        title,\n        uri\n    });\n});\n\nmsg._originalPayload = msg.payload;\nflow.set('latestMessage', msg);\n\nconst services = env.get('service');\nif(!services) {\n    node.status({\n        text: 'no services defined',\n        shape: 'ring',\n        fill: 'red'\n    });\n    return;    \n}\n\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            title: env.get('title'),\n            message: env.get('message'),\n            data: {\n                tag: flow.get('notificationTag'),\n                actions,\n                color: env.get(\"color\"),\n                group: env.get(\"group\"),\n                sticky: env.get(\"sticky\"),\n                timeout: env.get(\"timeout\"),\n                icon: env.get(\"icon\"),\n                priority: \"high\",\n                channel: \"alarms\"\n            }\n        }\n    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"noerr":0,"initialize":"const randomId = () => Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);\n\n[1,2,3].forEach(i => {\n    flow.set(`action${i}Id`, `action${i}_${randomId()}`);\n})\n\n\nflow.set('notificationTag', `${env.get('title')}_${randomId()}`);","finalize":"","libs":[],"x":298,"y":80,"wires":[["368c9723.5876f8"]]},{"id":"974bd48d.c253e8","type":"switch","z":"6dc0247c.d7210c","name":"which action?","property":"eventData.event.action","propertyType":"msg","rules":[{"t":"eq","v":"action1Id","vt":"flow"},{"t":"eq","v":"action2Id","vt":"flow"},{"t":"eq","v":"action3Id","vt":"flow"}],"checkall":"true","repair":false,"outputs":3,"x":1024,"y":176,"wires":[[],[],[]]},{"id":"204dbcfc.144ae4","type":"status","z":"6dc0247c.d7210c","name":"","scope":["f9e57204.71076","5bc7345c.07b1cc","a622c92a.2d9898","368c9723.5876f8"],"x":124,"y":272,"wires":[[]]},{"id":"5bc7345c.07b1cc","type":"function","z":"6dc0247c.d7210c","name":"build message","func":"const latestMessage = flow.get('latestMessage');\nconst event = msg.payload.event;\n\nlatestMessage.eventData = msg.payload;\nlatestMessage.payload = latestMessage._originalPayload;\ndelete latestMessage._originalPayload;\n\nif(env.get('userInfo')) {\n    const userData = msg.userData.find(u => u.id === msg.payload.context.user_id);\n    latestMessage.userData = userData;\n}\n\nif(msg.event_type === 'mobile_app_notification_cleared') {\n    node.status({\n        text: `cleared at: ${getPrettyDate()}`,\n        shape: 'dot',\n        fill: 'blue'\n    });\n    \n    return [null, latestMessage];\n}\n\nconst index = [1,2,3].find(i => event[`action_${i}_key`] === event.action);\nnode.status({\n    text: `${event[`action_${index}_title`]} at: ${getPrettyDate()}`,\n    shape: 'dot',\n    fill: 'green'\n});\n\nreturn latestMessage;\n\n\nfunction getPrettyDate() {\n    return new Date().toLocaleDateString('en-US', {\n        month: 'short',\n        day: 'numeric',\n        hour12: false,\n        hour: 'numeric',\n        minute: 'numeric',\n    });\n}","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":832,"y":176,"wires":[["974bd48d.c253e8"],[]]},{"id":"8d3bdc0c.37493","type":"switch","z":"6dc0247c.d7210c","name":"belongs here?","property":"payload.event.tag","propertyType":"msg","rules":[{"t":"eq","v":"notificationTag","vt":"flow"}],"checkall":"true","repair":false,"outputs":1,"x":432,"y":176,"wires":[["83ad2004.d04d"]]},{"id":"271e4479.b9249c","type":"ha-api","z":"6dc0247c.d7210c","name":"get user info","server":"","version":1,"debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\"type\": \"config/auth/list\"}","dataType":"json","responseType":"json","outputProperties":[{"property":"userData","propertyType":"msg","value":"","valueType":"results"}],"x":822,"y":128,"wires":[["5bc7345c.07b1cc"]]},{"id":"3618f055.6909a","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_cleared","server":"","version":3,"exposeAsEntityConfig":"","eventType":"mobile_app_notification_cleared","waitForRunning":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"},{"property":"event_type","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":194,"y":224,"wires":[["8d3bdc0c.37493"]]},{"id":"83ad2004.d04d","type":"switch","z":"6dc0247c.d7210c","name":"fetch user info?","property":"userInfo","propertyType":"env","rules":[{"t":"true"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":624,"y":176,"wires":[["271e4479.b9249c"],["5bc7345c.07b1cc"]]},{"id":"9d85d137.fe487","type":"switch","z":"6dc0247c.d7210c","name":"","property":"clear_notification","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","repair":false,"outputs":2,"x":143,"y":80,"wires":[["f9e57204.71076"],["a622c92a.2d9898"]],"l":false},{"id":"a622c92a.2d9898","type":"function","z":"6dc0247c.d7210c","name":"create clear notification","func":"const services = env.get('service');\nif(!services) {\n    node.status({\n        text: 'no services defined',\n        shape: 'ring',\n        fill: 'red'\n    });\n    return;    \n}\n\nservices.trim().split(/,\\s*/).forEach(service => {\n    if(!service) return;\n    \n    msg.payload = {\n        service,\n        data: {\n            message: \"clear_notification\",\n            data: {\n                tag: flow.get('notificationTag'),\n            }\n        }\n    };\n    node.send(msg);\n});\n\nnode.done();","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":318,"y":128,"wires":[["368c9723.5876f8"]]},{"id":"9bfe567c.3d10c8","type":"server-events","z":"6dc0247c.d7210c","name":"mobile_app_notification_action","server":"","version":3,"exposeAsEntityConfig":"","eventType":"mobile_app_notification_action","waitForRunning":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"},{"property":"event_type","propertyType":"msg","value":"$outputData(\"eventData\").event_type","valueType":"jsonata"}],"x":194,"y":176,"wires":[["8d3bdc0c.37493"]]},{"id":"368c9723.5876f8","type":"api-call-service","z":"6dc0247c.d7210c","name":"","server":"","version":5,"debugenabled":false,"domain":"notify","service":"notify","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"json","mergeContext":"callServiceData","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":530,"y":100,"wires":[[]]},{"id":"919173f3b6568e0f","type":"bigtimer","z":"8b43de8505f17be7","outtopic":"","outpayload1":"on","outpayload2":"off","name":"Sunset-Sunrise","comment":"","lat":"","lon":"","starttime":"5004","endtime":"5004","starttime2":0,"endtime2":0,"startoff":"0","endoff":"0","startoff2":0,"endoff2":0,"offs":0,"outtext1":"","outtext2":"","timeout":1440,"sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"day6":0,"month6":0,"day7":0,"month7":0,"day8":0,"month8":0,"day9":0,"month9":0,"day10":0,"month10":0,"day11":0,"month11":0,"day12":0,"month12":0,"d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"d6":0,"w6":0,"xday1":0,"xmonth1":0,"xday2":0,"xmonth2":0,"xday3":0,"xmonth3":0,"xday4":0,"xmonth4":0,"xday5":0,"xmonth5":0,"xday6":0,"xmonth6":0,"xday7":0,"xmonth7":0,"xday8":0,"xmonth8":0,"xday9":0,"xmonth9":0,"xday10":0,"xmonth10":0,"xday11":0,"xmonth11":0,"xday12":0,"xmonth12":0,"xd1":0,"xw1":0,"xd2":0,"xw2":0,"xd3":0,"xw3":0,"xd4":0,"xw4":0,"xd5":0,"xw5":0,"xd6":0,"xw6":0,"suspend":false,"random":false,"randon1":false,"randoff1":false,"randon2":false,"randoff2":false,"repeat":false,"atstart":false,"odd":false,"even":false,"x":620,"y":1000,"wires":[["479e4a5089231ba3"],[],[]]},{"id":"479e4a5089231ba3","type":"api-current-state","z":"8b43de8505f17be7","name":"Door 1 open?","server":"","version":3,"outputs":2,"halt_if":"Violated","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.elkm1_garage_oh_door_1","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"15","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":820,"y":1000,"wires":[["93094498664ec804"],[]]},{"id":"93094498664ec804","type":"subflow:6dc0247c.d7210c","z":"8b43de8505f17be7","name":"Manage Garage Door","env":[{"name":"service","value":"mobile_app_pixel_8","type":"str"},{"name":"title","value":"Garage Door","type":"str"},{"name":"message","value":"Garage Door is Open.<br>Do you want to close the door?","type":"str"},{"name":"action1Title","value":"Yes","type":"str"},{"name":"action2Title","value":"No","type":"str"},{"name":"group","value":"","type":"str"}],"x":1060,"y":1000,"wires":[["0b4d1337a31ae7ad"],["4ac099eab1b2f9d7"],[],[]]},{"id":"0b4d1337a31ae7ad","type":"api-call-service","z":"8b43de8505f17be7","name":"Close Garage Door","server":"","version":5,"debugenabled":false,"domain":"","service":"","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1290,"y":980,"wires":[[]]},{"id":"4ac099eab1b2f9d7","type":"delay","z":"8b43de8505f17be7","name":"Wait 30 Min","pauseType":"delay","timeout":"30","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1270,"y":1060,"wires":[["93094498664ec804"]]}]

Wow that is very cool. I did not know that Actionable Notifications thing existed (I’m still pretty new with NodeRed, just stumbling my way through it).

Any idea why the Sunset-Sunrise would be allowing the flow to trigger outside of that range? I can’t make sense of that one …

I’m not sure but the way mine works, I have on and off both set to sunset. And I unchecked everything at the bottom of the Bigtimmer node (the code I pasted reflects this).

Can you explain what you mean by you “have on and off both set to sunset?”

My logic would be that the flow “enables” at sunset – checks the status of the door. If the door is up, then start a timer and notify me in 15 minutes. If I close the door in that time frame, stop the timer and just keep monitoring the door status. If the door opens, start that timer again. And so on …

So I have the “repeat output” checked - my thinking was that the “sunrise to sunset” would ONLY trigger a change exactly at sunrise and sunset. Thus, if I open the door 1 minute after sunset, the notification would never be sent. Or maybe I’m wrong?

image

My logic is to check the status of the door (current state node) and if it is open for 15 minutes, send the notification.

image

So, this will not work after the door is closed and opened again.

Edit. So, you would always want to monitor the state of the garage door and only take action between sunrise and sunset. Don’t use bigtimmer

image

I just want to mention that home assistant has a sunrise/sunset sensor built in. It can be used as trigger:

Screenshot 2024-02-07 184720

If you leave the state blank it will fire once at sunrise and sunset.

In a current state node as a condition.

Screenshot 2024-02-07 184901

I have a feeling the error is with Bigtimer. I’ve seen other threads where people had issues with it as well.

Yes - you are correct. I’d want to monitor the state of the door and ignore it when it’s daylight.

Thanks a lot - I figured there was a better way to do this, it’s always helpful to have someone else check your logic and (sometimes!) suggest a better way.

Gotta play with that actionable notifcation node. SO much easier than what I was trying to do!

Thanks again

Thanks @Mikefila. This is helpful as well.

I’m coming from HomeSeer so the logic is similar in a lot of ways, but there is SO much more flexibility in what you can do in HA vs. HS. I’m happier every day that i made the leap.

NodeRed helps a lot as well. I looked back at my old HS instance - I had about 15 automations controlling 3 garage doors. Keeping them straight was crazy. NR not only makes it easier to “think through” but also makes things easier to understand and visualize. It’s great.

Appreciate your input here - that is very helpful.

rmf

This worked great - although I’m still struggling with getting the Actionable Notification to work. Doesn’t seem to want to recognize my device. I’ll have to do some more digging to understand that. But I have a more complex flow that works now with your suggestion of the door event triggering and the time range nodes.

Thanks again!

Are you using the Companion App on your device? What are the error messages? Anything in the log files?