Complex time based schedule for roller shutter: sun event + static time

Hi All,

I'm newbie to Node-RED, but really enjoying to use it. I'm successful with several automation, but not able to solve this issue:

I want to have a schedule for my roller shutters. I want to trigger the "UP" command if the time is 'Sunrise', but never earlier then a static - let's say 7:00am - time. If the sunrise is earlier then 7:00 (summer) then I want to fire at 7:00.

[{"id":"e23468e5.2814c8","type":"inject","z":"ff26ca4c.e731c8","name":"7:00am","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 07 * * *","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":220,"y":4340,"wires":[[]]},{"id":"ed0ab51e.e9d5a8","type":"eztimer","z":"ff26ca4c.e731c8","name":"","debug":false,"autoname":"sunrise","tag":"eztimer","topic":"","suspended":false,"sendEventsOnSuspend":false,"latLongSource":"haZone","latLongHaZone":"zone.home","lat":"47.6148426766379","lon":"19.355335235595707","timerType":"2","startupMessage":false,"ontype":"1","ontimesun":"sunrise","ontimetod":"17:00","onpropertytype":"msg","onproperty":"payload","onvaluetype":"num","onvalue":1,"onoffset":0,"onrandomoffset":0,"onsuppressrepeats":false,"offtype":"1","offtimesun":"sunrise","offtimetod":"dusk","offduration":"00:01:00","offpropertytype":"msg","offproperty":"payload","offvaluetype":"num","offvalue":0,"offoffset":0,"offrandomoffset":0,"offsuppressrepeats":false,"resend":false,"resendInterval":"0s","mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"sun":true,"x":217,"y":4400,"wires":[[]]},{"id":"a1cf739a.5624e","type":"debug","z":"ff26ca4c.e731c8","name":"Fire 'Roller down' scripts","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":820,"y":4380,"wires":[]},{"id":"e988ac46.ed02d8","type":"comment","z":"ff26ca4c.e731c8","name":"Logic: If (Sunrise is earlier then the static time) then (fire at 7:00), else (fire at 'sunrise')","info":"","x":540,"y":4300,"wires":[]}]

Can someone help me?

You can use a gate node that is controlled by your inject node. You could make the gate open at 7:00 am (and closes at noon). When the gate is open it will let the messages pass along

But I see some missing use cases in your example:

  • when shall your roller shutters go DOWN? And how do you control or automate that? If manual, ok, fine
  • you need to insert a repeater function to handle the UP command. Imagine the sunrise is before 7:00 am. The UP command will be triggered but blocked by the closed gate. Once the gate opens, the command that was sent earlier is gone. To handle this you can use a trigger node set to repeat last command once every minute or so

This is a small simulator I just drawed in the editor. You can simulate sunrise (and sunset) and see how it would work. I added a part just to show how you also could handle the sunset annd automatic closing of the roller shutters

The "sunrise" flow works like this:

  • the gate is opened at a defined hour like 7:00 am
  • if sunrise has already happened, the sunrise command waiting in the trigger node will soon fire and pass through the open gate, the change node is setting the proper "UP" command needed and you can study the result in the debug view. I did configure trigger nodes to fire every ten second to make it easier to test, you could of course change this, typically to once per minute. If you do not want the message being repeated every minute (to synchronize the state and position of your shutters), simply insert the rbe node in the chain
  • the gate closes at a defined time like 12:00 am, messages are blocked

The "sunset" flow works the same but with different time settings and with "DOWN" as final command

These flows are just one example of, I assume, many possible ways but at least it gives some ideas how you can do it

EDIT: the gate node used in this example is the node-red-contrib-simple-gate

[{"id":"93738d51.24d45","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 07 * * *","once":false,"onceDelay":0.1,"topic":"control","payload":"open","payloadType":"str","x":450,"y":250,"wires":[["f05a4eeb.fb6f5"]]},{"id":"3fb556ea.d5694a","type":"debug","z":"c25ba5de.f73df8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1250,"y":270,"wires":[]},{"id":"f05a4eeb.fb6f5","type":"gate","z":"c25ba5de.f73df8","name":"","controlTopic":"control","defaultState":"open","openCmd":"open","closeCmd":"close","toggleCmd":"toggle","defaultCmd":"default","statusCmd":"status","persist":false,"x":730,"y":270,"wires":[["bca16a67.1a7798"]]},{"id":"b1b3fdb5.ae42a","type":"trigger","z":"c25ba5de.f73df8","name":"","op1":"","op2":"","op1type":"nul","op2type":"pay","duration":"10","extend":false,"overrideDelay":false,"units":"s","reset":"close","bytopic":"all","topic":"topic","outputs":1,"x":730,"y":360,"wires":[["62aa018c.a9ef7","f05a4eeb.fb6f5"]]},{"id":"62aa018c.a9ef7","type":"switch","z":"c25ba5de.f73df8","name":"","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":730,"y":420,"wires":[["b1b3fdb5.ae42a"]]},{"id":"93cf1f2e.1cc63","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 12 * * *","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":450,"y":300,"wires":[["f05a4eeb.fb6f5","b1b3fdb5.ae42a"]]},{"id":"8a05c0e8.ccd57","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"sunrise","payloadType":"str","x":480,"y":360,"wires":[["b1b3fdb5.ae42a","f05a4eeb.fb6f5"]]},{"id":"6e8d069c.4f7368","type":"rbe","z":"c25ba5de.f73df8","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":1080,"y":320,"wires":[["3fb556ea.d5694a"]]},{"id":"bca16a67.1a7798","type":"change","z":"c25ba5de.f73df8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"UP","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":270,"wires":[["3fb556ea.d5694a"]]},{"id":"1f6187af.0c21e8","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 18 * * *","once":false,"onceDelay":0.1,"topic":"control","payload":"open","payloadType":"str","x":450,"y":540,"wires":[["fe14a64.10d0658"]]},{"id":"21108a25.df3d26","type":"debug","z":"c25ba5de.f73df8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1250,"y":560,"wires":[]},{"id":"fe14a64.10d0658","type":"gate","z":"c25ba5de.f73df8","name":"","controlTopic":"control","defaultState":"open","openCmd":"open","closeCmd":"close","toggleCmd":"toggle","defaultCmd":"default","statusCmd":"status","persist":false,"x":730,"y":560,"wires":[["300daeb.7564652"]]},{"id":"b06bcf3d.83c43","type":"trigger","z":"c25ba5de.f73df8","name":"","op1":"","op2":"","op1type":"nul","op2type":"pay","duration":"10","extend":false,"overrideDelay":false,"units":"s","reset":"close","bytopic":"all","topic":"topic","outputs":1,"x":730,"y":650,"wires":[["97ed8e51.36bc3","fe14a64.10d0658"]]},{"id":"97ed8e51.36bc3","type":"switch","z":"c25ba5de.f73df8","name":"","property":"payload","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":730,"y":710,"wires":[["b06bcf3d.83c43"]]},{"id":"91dc4d6d.22637","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 23 * * *","once":false,"onceDelay":0.1,"topic":"control","payload":"close","payloadType":"str","x":450,"y":590,"wires":[["fe14a64.10d0658","b06bcf3d.83c43"]]},{"id":"4d244296.dde0dc","type":"inject","z":"c25ba5de.f73df8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"sunset","payloadType":"str","x":480,"y":650,"wires":[["b06bcf3d.83c43","fe14a64.10d0658"]]},{"id":"3b1891d0.86c81e","type":"rbe","z":"c25ba5de.f73df8","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":1080,"y":610,"wires":[["21108a25.df3d26"]]},{"id":"300daeb.7564652","type":"change","z":"c25ba5de.f73df8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"DOWN","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":910,"y":560,"wires":[["21108a25.df3d26"]]}]

Here is a simple solution.

image

How it works...

There are 3 schedules in the cron plus node.

  1. 7am
  2. sunrise
  3. 1am

The function checks a context flag block. If block == false, the function will allow the msg to pass and set block to true.

at 1am, block is reset to false.

Simple.

The function code...

var block = context.get("block") == true;
switch (msg.topic) {
    case "7am":
    case "sunrise":
        if (block == false) {
            node.send(msg);
        }
        block = true;
        break;
    case "1am":
        block = false;
        break;        
}
context.set("block", block);
return null;

The 3 schedules...

The flow...

[{"id":"8c10eeca.a7ec3","type":"cronplus","z":"83a23089.dc697","name":"7am","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"7am","topic":"7am","payloadType":"default","payload":"","expressionType":"cron","expression":"0 0 7 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"},{"name":"sunrise","topic":"sunrise","payloadType":"default","payload":"","expressionType":"solar","expression":"0 * * * * * *","location":"54.999171308674555 -1.417257711473212","offset":"0","solarType":"selected","solarEvents":"sunrise"},{"name":"1am","topic":"1am","payloadType":"default","payload":"","expressionType":"cron","expression":"0 0 1 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":730,"y":700,"wires":[["b36dfa89.f1cd88"]]},{"id":"a7c57ecd.33856","type":"debug","z":"83a23089.dc697","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1150,"y":700,"wires":[]},{"id":"b36dfa89.f1cd88","type":"function","z":"83a23089.dc697","name":"block/allow","func":"var block = context.get(\"block\") == true;\nswitch (msg.topic) {\n    case \"7am\":\n    case \"sunrise\":\n        if (block == false) {\n            node.send(msg);\n        }\n        block = true;\n        break;\n    case \"1am\":\n        block = false;\n        break;\n    default:\n        \n}\ncontext.set(\"block\", block);\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":700,"wires":[["a7c57ecd.33856"]]}]

Thanks krambriw!
Wow, great, this is exactly what I want to implement!
Could you please specify what is 'gate' node? I don't have it as built in, and if I search for this, I have 50+ gate node. Which one should I select to be able to use your example?

Thanks Steve-Mcl,
Also a great simple solution. With this I have to change only in one place the fixed date (7:00) if needed, so seems a bit more simpler. I will test it also, most probably solve my problem.

You would simply change the Cron expression of the "7am" schedule entry to the time you want. The rest will just work.

Don't work if you are not familiar with Cron, there is an expression builder (and a tooltip to show what your Cron will do and the built in help has lots of examples)

Ps, I just edited and corrected a mistake in case you have imported already.

PS2, you will want to set the payload of the 7am and sunrise schedules to send a string UP.

Well, at least classic linux Cron isn't new for me, only the whole home-automation world + I'm not familiar with java script :slight_smile:
Thanks for the heads-up, I re-imported already the newer version.

Could you please explain what happens if sunrise is after 7:00?
In your script case "7am": is without any action, I don't understand how works is sunrise is earlier or later then 7. I just want to learn.

Ah. I've just realised it doesn't meet your criteria. Haha. Sorry. I interpreted your request as being sunrise or 7 a.m.

Ginnie 5 mins.

The criteria: At sunrise if sunrise is after 7, else at 7.

Here you go (and I added test inject times so you can test its correct)...

[{"id":"8c10eeca.a7ec3","type":"cronplus","z":"83a23089.dc697","name":"7am","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"morning","topic":"morning","payloadType":"str","payload":"UP","expressionType":"cron","expression":"0 0 7 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"},{"name":"sunrise","topic":"sunrise","payloadType":"str","payload":"UP","expressionType":"solar","expression":"0 * * * * * *","location":"54.999171308674555 -1.417257711473212","offset":"0","solarType":"selected","solarEvents":"sunrise"},{"name":"unblock","topic":"unblock","payloadType":"default","payload":"","expressionType":"cron","expression":"0 0 1 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":730,"y":700,"wires":[["b36dfa89.f1cd88"]]},{"id":"a7c57ecd.33856","type":"debug","z":"83a23089.dc697","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1150,"y":700,"wires":[]},{"id":"b36dfa89.f1cd88","type":"function","z":"83a23089.dc697","name":"block/allow","func":"var block = context.get(\"block\") == true;\nvar now = new Date();\nif (msg._testTime) now = new Date(msg._testTime);//debuggiing / delete me later\n\nif (now.getHours() >= 7 && block == false) {\n    node.send(msg);\n    block = true;\n}\n\nif (msg.topic == \"unblock\") {\n    block = false;\n}\n\ncontext.set(\"block\", block);\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":700,"wires":[["a7c57ecd.33856"]]},{"id":"5ee9d7d8.962528","type":"inject","z":"83a23089.dc697","name":"7am","props":[{"p":"_testTime","v":"2021-04-03T07:00:00.000Z","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"morning","payload":"UP","payloadType":"str","x":630,"y":520,"wires":[["b36dfa89.f1cd88"]]},{"id":"6e8c70dc.4a81d","type":"inject","z":"83a23089.dc697","name":"sunrise 6:45am","props":[{"p":"_testTime","v":"2021-04-03 06:45","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"sunrise","payload":"UP","payloadType":"str","x":660,"y":560,"wires":[["b36dfa89.f1cd88"]]},{"id":"d130f299.3a8a6","type":"inject","z":"83a23089.dc697","name":"1am","props":[{"p":"_testTime","v":"2021-04-03T01:00:00.000Z","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"unblock","x":630,"y":640,"wires":[["b36dfa89.f1cd88"]]},{"id":"21386be8.a9ae34","type":"inject","z":"83a23089.dc697","name":"sunrise 7:15am","props":[{"p":"_testTime","v":"2021-04-03 07:15","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"sunrise","payload":"UP","payloadType":"str","x":660,"y":600,"wires":[["b36dfa89.f1cd88"]]}]

Wow, you are a brain...
Is it possible to remove the "hardcoded" 7 from line 5 and use the 'morning' value from the Cron?
I just want to keep it easily manageable later also, and ensure that I have to change the timing only in one place. It it is too difficult just tell me (unfortunatelly I can't judge and can't make it)

What you have to remember, node-red is event based (stuff happens that makes other stuff happen) AND each node is its own entity (i.e. the function node cannot query a value in another node)

A Kinda workaround....
Because there are 2 schedules that could potentially trigger it, you need to send this "7" with both the morning schedule and the sunrise schedule. As a demo, i put it in payload.

It works but I dont like it. Up to you.

[{"id":"8c10eeca.a7ec3","type":"cronplus","z":"83a23089.dc697","name":"7am or sunrise","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[{"name":"morning","topic":"morning","payloadType":"num","payload":"7","expressionType":"cron","expression":"0 0 7 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"},{"name":"sunrise","topic":"sunrise","payloadType":"num","payload":"7","expressionType":"solar","expression":"0 * * * * * *","location":"54.999171308674555 -1.417257711473212","offset":"0","solarType":"selected","solarEvents":"sunrise"},{"name":"unblock","topic":"unblock","payloadType":"default","payload":"","expressionType":"cron","expression":"0 0 1 * * * *","location":"","offset":"0","solarType":"all","solarEvents":"sunrise,sunset"}],"x":1520,"y":340,"wires":[["b36dfa89.f1cd88","2cd62e00.190f82"]]},{"id":"a7c57ecd.33856","type":"debug","z":"83a23089.dc697","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1790,"y":280,"wires":[]},{"id":"b36dfa89.f1cd88","type":"function","z":"83a23089.dc697","name":"block/allow","func":"var block = context.get(\"block\") == true;\nvar now = new Date();\nif (msg._testTime) now = new Date(msg._testTime);//debuggiing / delete me later\n\nif (now.getHours() >= msg.payload && block == false) {\n    msg.payload = \"UP\";\n    node.send(msg);\n    block = true;\n}\n\nif (msg.topic == \"unblock\") {\n    block = false;\n}\n\ncontext.set(\"block\", block);\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1770,"y":340,"wires":[["a7c57ecd.33856"]]},{"id":"5ee9d7d8.962528","type":"inject","z":"83a23089.dc697","name":"7am","props":[{"p":"_testTime","v":"2021-04-03T07:00:00.000Z","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"morning","payload":"7","payloadType":"num","x":1490,"y":160,"wires":[["b36dfa89.f1cd88"]]},{"id":"6e8c70dc.4a81d","type":"inject","z":"83a23089.dc697","name":"sunrise 6:45am","props":[{"p":"_testTime","v":"2021-04-03 06:45","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"sunrise","payload":"7","payloadType":"num","x":1520,"y":200,"wires":[["b36dfa89.f1cd88"]]},{"id":"d130f299.3a8a6","type":"inject","z":"83a23089.dc697","name":"1am","props":[{"p":"_testTime","v":"2021-04-03T01:00:00.000Z","vt":"str"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"unblock","x":1490,"y":280,"wires":[["b36dfa89.f1cd88"]]},{"id":"21386be8.a9ae34","type":"inject","z":"83a23089.dc697","name":"sunrise 7:15am","props":[{"p":"_testTime","v":"2021-04-03 07:15","vt":"str"},{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"sunrise","payload":"7","payloadType":"num","x":1520,"y":240,"wires":[["b36dfa89.f1cd88"]]},{"id":"2cd62e00.190f82","type":"debug","z":"83a23089.dc697","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1670,"y":440,"wires":[]}]

Thanks, I like this version, too.
Is 7 equals to 7:00, and I can use payloads like 6:50, 7:20, 18:30, etc?
I learned a lot today evening. :slight_smile:

Not quite. That is more tricky.

You would need to check against 2 parts - hour and minute.

See if you can figure it out.

Clue, send a JSON Payload with 2 properties. Then in the function, compare to the 2 properties you entered in the payload.

Got it, however I'm too new for this and I have too much new information. The solution should be something like this:

if (((now.getHours() = msg.payload (hour_part) AND now.getMinutes() >= msg.payload (minute_part)) OR (now.getHours() >= msg.payload)) && block == false)

Also to send JSON and properties are new for me :smiley:
I have to learn a lot.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.