configurable delay node

1,200 views
Skip to first unread message

Venkatesh E

unread,
Mar 18, 2017, 11:58:48 AM3/18/17
to Node-RED
Hi,

I have situation where I need to dynamically configure the delay node using properties in a function (bypassing UI set values).

I have a MQTT node which receives "true" to turn-on a light and using a delay node (fixed @ 2 minutes) and then turn-off's light

MQTT  (Turn-ON-Light) --> delay (2 min) --> MQTT (Turn-Off-light)

I have another MQTT topic which sends the delay value and I am looking at configuring the delay node with this value stored in flow/global context.

So currently delay works for fixed delay value set in UI/default properties, but I am unable to find the properties to set the delay value dynamically in function (if at all possible).
Or is there any better node I can use for this situation.

Let me know. 
Thanks
Venkat

Dave C-J

unread,
Mar 18, 2017, 2:02:10 PM3/18/17
to node...@googlegroups.com
Hi, the delay node can't be configured dynamically. It would be possible to create a function node to do it fairly easily using setTimeout for the delay and local context to hold the delay value.

st...@urban-gro.com

unread,
Mar 20, 2017, 6:45:46 PM3/20/17
to Node-RED
Hello Dave - 

I've been looking for this exact scenario - Can you explain a little further either in working or pseudo code? I'm having a bit of trouble wrapping my head around global and flow context - and then applying it. 

In my scenario I am using the node-red dashboard suite to allow the user to control a slider - which then feeds out a 0-60 value for seconds that I am trying to then feed into a dynamic delay flow - (based on an irrigation example where zone 1 irrigates, then zone 2, then zone 3 - where right now a trigger causes a 10 second delay that is not configurable dynamically without resetting the flow). 

Like Venkat - I am trying to allow a dynamic delay it seems. 

Thanks for any input!

Dave C-J

unread,
Mar 21, 2017, 4:56:09 AM3/21/17
to node...@googlegroups.com
Here is a simple proof of concept... the top branch sets the delay (in this case to a random number between 1 and 10 secs)
the bottom is the incoming main message that will be delayed.  The key is the top branch sets it's topic to "delay". This could easily be done by the slider also.

[{"id":"2ebb490f.c08b66","type":"inject","z":"5abb0ba2.b32384","name":"","topic":"delay","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"x":137,"y":850,"wires":[["f8c7fd6c.1b978"]]},{"id":"9a27d72a.88f968","type":"function","z":"5abb0ba2.b32384","name":"settable delay","func":"var delay = context.delay || 0;\nif (msg.topic === \"delay\") {\n    delay = msg.payload * 1000;\n    context.delay = delay;\n    node.warn(\"Set delay to \"+delay+\" seconds\");\n}\nelse {\n    setTimeout(function() {\n        node.send(msg);\n    }, delay);\n}\nreturn null;","outputs":1,"noerr":0,"x":411,"y":966,"wires":[["26f97a8a.f4ee06"]]},{"id":"26f97a8a.f4ee06","type":"debug","z":"5abb0ba2.b32384","name":"","active":true,"console":"false","complete":"false","x":652,"y":965,"wires":[]},{"id":"f8c7fd6c.1b978","type":"random","z":"5abb0ba2.b32384","name":"","low":"1","high":"10","inte":"true","x":282.5,"y":888,"wires":[["9a27d72a.88f968"]]},{"id":"89a6b5f4.23f028","type":"inject","z":"5abb0ba2.b32384","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":141,"y":967,"wires":[["9a27d72a.88f968"]]}]

Steve_W

unread,
Mar 21, 2017, 3:43:17 PM3/21/17
to Node-RED
Dave, 

Thanks for the help - between what I could scour off the web and your advice, I've pasted what I came up with below. I'm not 100% sure that data will persist across flow-restarts, and from what it sounds like it definitely won't persist across node-red restarts. I think the next step is for me to figure out how to store the data in a local JSON file or something of that sort to read on node-red and flow restarts, and subsequently update when a user changes the slider input value. 

[{"id":"57539c97.7ae334","type":"inject","z":"8ddf478f.54e458","name":"","topic":"delay","payload":"10","payloadType":"num","repeat":"","crontab":"","once":false,"x":386,"y":200,"wires":[["11d9bb57.779965"]]},{"id":"11d9bb57.779965","type":"function","z":"8ddf478f.54e458","name":"settable delay","func":"var delay = flow.get('delay') || 0;\nvar delay_display = flow.get('delay_display') || 0;\n\nif (msg.topic === \"delay\") {\n    delay = msg.payload * 1000;\n    flow.set('delay', delay);\n    flow.set('delay_display', delay/1000);\n    node.status({fill:\"grey\",shape:\"dot\",text:flow.get('delay_display') + \" second delay\"});\n}\n\nelse {\n    node.status({fill:\"red\",shape:\"dot\",text:\"delaying...\"});\n    setTimeout(function() {\n        node.status({fill:\"grey\",shape:\"dot\",text:flow.get('delay_display') + \" second delay\"});\n        node.send(msg);\n    }, flow.get('delay'));\n}\n\nreturn null;","outputs":1,"noerr":0,"x":573,"y":261,"wires":[["3a33f8f8.c8c538"]]},{"id":"3a33f8f8.c8c538","type":"debug","z":"8ddf478f.54e458","name":"","active":true,"console":"false","complete":"false","x":754,"y":261,"wires":[]},{"id":"9ad3e8cf.b5bb78","type":"inject","z":"8ddf478f.54e458","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":389,"y":314,"wires":[["11d9bb57.779965"]]}]


 

Julian Knight

unread,
Mar 21, 2017, 4:36:05 PM3/21/17
to Node-RED
That looks right. It will survive a flow restart but not a Node-RED restart. I don't think you need to worry though since if you are restarting node-red, the delay will be longer than 10s anyway!

If you really did want to persist, you would need to write the values to permanent storage, a file perhaps or MQTT.

Something like this:

[{"id":"4a685cfa.665de4","type":"inject","z":"9974253c.de8db8","name":"","topic":"CONFIG/delay","payload":"10","payloadType":"num","repeat":"","crontab":"","once":false,"x":454.1111145019531,"y":759.5555114746094,"wires":[["1c991d13.ef7cb3"]]},{"id":"77148901.fe99c8","type":"function","z":"9974253c.de8db8","name":"settable delay","func":"var delay = flow.get('delay') || 0;\nvar delay_display = flow.get('delay_display') || 0;\n\nif (msg.topic === \"CONFIG/delay\") {\n    delay = parseInt( msg.payload * 1000 );\n    flow.set('delay', delay);\n    flow.set('delay_display', delay/1000);\n    node.status({fill:\"grey\",shape:\"dot\",text:flow.get('delay_display') + \" second delay\"});\n}\n\nelse {\n    node.status({fill:\"red\",shape:\"dot\",text:\"delaying...\"});\n    setTimeout(function() {\n        node.status({fill:\"grey\",shape:\"dot\",text:flow.get('delay_display') + \" second delay\"});\n        node.send(msg);\n    }, flow.get('delay'));\n}\n\nreturn null;","outputs":1,"noerr":0,"x":611.1111145019531,"y":909.5555419921875,"wires":[["8c0d193d.9eea98"]]},{"id":"8c0d193d.9eea98","type":"debug","z":"9974253c.de8db8","name":"","active":true,"console":"false","complete":"false","x":792.1111145019531,"y":909.5555419921875,"wires":[]},{"id":"f5491045.4b18e","type":"inject","z":"9974253c.de8db8","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":427.1111145019531,"y":962.5555419921875,"wires":[["77148901.fe99c8"]]},{"id":"1c991d13.ef7cb3","type":"mqtt out","z":"9974253c.de8db8","name":"","topic":"","qos":"1","retain":"true","broker":"3b2726f7.c4d8da","x":636.3888854980469,"y":760.8750228881836,"wires":[]},{"id":"36ad9445.8778dc","type":"mqtt in","z":"9974253c.de8db8","name":"","topic":"CONFIG/delay","qos":"2","broker":"3b2726f7.c4d8da","x":425.3888854980469,"y":837.3333358764648,"wires":[["77148901.fe99c8"]]},{"id":"3b2726f7.c4d8da","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"Pi2_NR-Live","usetls":false,"verifyservercert":true,"compatmode":false,"keepalive":"15","cleansession":true,"willTopic":"DEVICES/PI2NR-LIVE","willQos":"1","willRetain":"false","willPayload":"Offline","birthTopic":"DEVICES/PI2NR-LIVE","birthQos":"1","birthRetain":"false","birthPayload":"Online"}]

Note the slight changes to the function. Topic has changed slightly and the use of parseInt. Not tested that code so take with a pinch of salt.

Also, personally, I'd separate the function node into two. One connected to the MQTT input and solely responsible for setting the flow vars. The other solely responsible for getting the flow vars and controlling the delay.

That way you are decoupling the code which makes both debugging and future changes easier. It is also (arguably) easier to follow the logic.

Jota De Picas

unread,
Sep 17, 2017, 7:26:40 PM9/17/17
to Node-RED
I know it's not very clean, and probably it won't apply to all cases, but I think you could create a reusable subflow that consists of a switch node + a number of fixed delay nodes, and you could select which one to apply based on some property on the input msg, like "msg.customDelay", or something like that.

This has the obvious disadvantage of still relying on pre-fixed delays, but you could add any number of them, let's say 10 different delays from 1 to 10 minutes, or seconds, etc.
While still limited, I think this could be a more robust/simpler option in some cases.
Reply all
Reply to author
Forward
0 new messages