Can I have a delay / sleep inside Function node

Hi Craig, I enjoyed your tip.
My impression: the DSM node behavior is mostly determined by methods rather than current state. isn't it?
Here the flow to operate a blind with DSM node. I've used only "timeout" function (that works as a callback). So far I could not find useful the version with "timer" method.
I'm wondering howto use "timer" method since I couldn't group statements. I just tries with the "do" construct that accepts only one statement. Therefore I was only able to operate a "resume" redirecting to an other method. Do you have any tips here? Please, let me know if an additional flow could be useful to give you a better idea.

Compare to former flow (w/o DSM node) this one results simpler but I had to cancel the "seamless" feature. Therefore if the user moves the slider in several steps (same direction) then several glitches will affect the motor.
I could mitigate this issue setting "only on release" the "Output" parameter of the slider node (this point answers to Colin as well)
I want to report a possible bug on slider node:
selecting option "If msg arrives on input, pass through to output:" I could detect the slider node output only if I inject the payload type "flow" or "global". But only a number type will initialize the slider node as desired. Unfortunately I do not detect the output for remaining types. Or only randomly if I select the payload type "number". Have I missed something? or just jumped into a bug?

Question: is it possible to get the value of a running "timeout" function? Same question for the "timer" method.

To mitigate the sudden sliding inversion I introduced a 250ms pause.

Ciao

[{"id":"bc4a0c48.3ac56","type":"dsm","z":"10258155.fcd03f","name":"BlindControl","sm_config":"{\n    \"stateOutput\": \"state\",\n    \"currentState\": \"onHalt\",\n    \"states\": {\n        \"onHalt\": {\n            \"power\": \"active\"\n        },\n        \"active\": {\n            \"complRunAndCheck\": \"onHalt\"\n        }\n    },\n    \"data\": {\n        \"temp\": 0,\n        \"current\": 0,\n        \"UP\": 1,\n        \"DOWN\": 1,\n        \"runTime\": 0\n    },\n    \"methods\": {\n        \"temp\": {\n            \"name\": \"setData\"\n        },\n        \"current\": {\n            \"name\": \"setData\"\n        },\n        \"UP\": {\n            \"name\": \"setData\"\n        },\n        \"DOWN\": {\n            \"name\": \"setData\"\n        },\n        \"runTime\": {\n            \"name\": \"setData\"\n        },\n        \"setPoint\": [\n            \"sm.data.temp = msg.payload;\",\n            \"if (sm.currentState === 'active') {\",\n            \"   output = false;\",\n            \"} else {\",            \n            \"   if (msg.payload > sm.data.current) {\",\n            \"       sm.data.UP = 0 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       sm.direction = 1 ;\",\n            \"       sm.data.runTime = msg.payload - sm.data.current;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'green';\",\n            \"       sm.text = 'activeLift';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else if (msg.payload < sm.data.current) {\",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 0 ;\",\n            \"       sm.direction = -1 ;\",\n            \"       sm.data.runTime = sm.data.current - msg.payload;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'red';\",\n            \"       sm.text = 'activeLower';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else { \",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       output = true;\",\n            \"   } \",\n            \"} \"\n        ],\n        \"power\": [\n            \"timeout.endRum = setTimeout(function() {\",\n            \"   sm.data.UP = 1;\",\n            \"   sm.data.DOWN = 1;\",\n            \"   sm.fill = 'grey';\",\n            \"   sm.text = 'onHalt';\",\n            \"   resume('complRunAndCheck', msg);\",\n            \"}, sm.data.runTime);\",\n            \"output = false;\"\n            ],\n        \"complRunAndCheck\": [\n            \"if (sm.data.temp < sm.data.current && sm.direction === 1 || sm.data.temp > sm.data.current && sm.direction === -1) {\",\n            \"   timeout.changeDir = setTimeout(function() {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"   }, 250);\",\n            \"} else if (sm.data.temp != sm.data.current) {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"} else {\",\n            \"}\"\n            ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.fill\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"sm.text\"\n            }\n        }\n    }\n}","x":730,"y":200,"wires":[["667a7c2b.da8254"]]},{"id":"50c67c46.c3ce24","type":"ui_slider","z":"10258155.fcd03f","name":"BlindHeightWidget","label":"{{ value }}","group":"deb628f5.33b0e8","order":1,"width":"6","height":"1","passthru":false,"topic":"setPoint","min":"0","max":"10","step":"1","x":320,"y":200,"wires":[["8f71e891.402e58"]],"outputLabels":["setpoint"]},{"id":"e5865eac.1d9f4","type":"inject","z":"10258155.fcd03f","name":"init","topic":"setPoint","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":260,"wires":[["50c67c46.c3ce24","8a96a614.b89518","8f71e891.402e58"]]},{"id":"8a96a614.b89518","type":"function","z":"10258155.fcd03f","name":"BlindProfile","func":"msg1={};\nmsg2={};\nmsg3={};\nmsg4={};\nmsg5={};\n// var unoProfile = { step:0, max:0 };\nmsg1.topic = \"temp\";\nmsg1.payload = 0;\nmsg2.topic = \"current\";\nmsg2.payload = 0;\nmsg3.topic = \"UP\";\nmsg3.payload = 1;\nmsg3.topic = \"DOWN\";\nmsg3.payload = 1;\nmsg2.topic = \"runTime\";\nmsg2.payload = 0;\nreturn [ msg1, msg2, msg3, msg4, msg5];","outputs":5,"noerr":0,"x":507,"y":260,"wires":[["bc4a0c48.3ac56"],["bc4a0c48.3ac56"],["bc4a0c48.3ac56"],["bc4a0c48.3ac56"],["bc4a0c48.3ac56"]]},{"id":"667a7c2b.da8254","type":"function","z":"10258155.fcd03f","name":"RpiPinDist","func":"msg1={};\nmsg2={};\nmsg1.payload = msg.data.UP;\nmsg2.payload = msg.data.DOWN;\nreturn [ msg1, msg2 ];","outputs":2,"noerr":0,"x":741,"y":280,"wires":[["a826b719.7a8d08"],["8b800e81.bb303"]]},{"id":"a826b719.7a8d08","type":"rpi-gpio out","z":"10258155.fcd03f","name":"","pin":"32","set":true,"level":"1","freq":"","out":"out","x":922,"y":234,"wires":[],"inputLabels":["Window#1"]},{"id":"8b800e81.bb303","type":"rpi-gpio out","z":"10258155.fcd03f","name":"","pin":"36","set":true,"level":"1","freq":"","out":"out","x":930,"y":280,"wires":[],"inputLabels":["Window#1"]},{"id":"8f71e891.402e58","type":"function","z":"10258155.fcd03f","name":"adaptBlindOne","func":"const MAX = 27000\nmsg.payload = msg.payload*MAX/10;\nreturn msg;","outputs":1,"noerr":0,"x":510,"y":100,"wires":[["bc4a0c48.3ac56"]]},{"id":"deb628f5.33b0e8","type":"ui_group","z":"","name":"GO-UNO","tab":"7e3910f.9132af","disp":true,"width":"6","collapse":false},{"id":"7e3910f.9132af","type":"ui_tab","z":"","name":"TheBridgeBlinds","icon":"dashboard"}]
`

Hi again, this flow reduces the risk to let the user changes by mistake the slider setting.
I did not find out howto dim/disable the widget. Does NodeRed allow for that?
I could not be able to hide the single widget but the whole group. Is it possible?
Additionally the web page mixes up every time the text replaces the slider. Does NodeRed allow for a better front end display? How?
thx/ciao, s.

[{"id":"1124b68a.7a4729","type":"change","z":"667bf9ae.4b2b98","name":"SliderMgnt","rules":[{"t":"change","p":"payload","pt":"msg","from":"display","fromt":"str","to":"{\"group\":{\"show\":[\"TheBridgeBlinds_GO-ONE\"]}}","tot":"json"},{"t":"change","p":"payload","pt":"msg","from":"hide","fromt":"str","to":"{\"group\":{\"hide\":[\"TheBridgeBlinds_GO-ONE\"]}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":340,"wires":[["105fdd39.e87d93"]]},{"id":"4a4a64cb.a7e85c","type":"change","z":"667bf9ae.4b2b98","name":"MsgMgnt","rules":[{"t":"change","p":"payload","pt":"msg","from":"display","fromt":"str","to":"{\"group\":{\"show\":[\"TheBridgeBlinds_Msg1\"]}}","tot":"json"},{"t":"change","p":"payload","pt":"msg","from":"hide","fromt":"str","to":"{\"group\":{\"hide\":[\"TheBridgeBlinds_Msg1\"]}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":300,"wires":[["105fdd39.e87d93"]]},{"id":"105fdd39.e87d93","type":"ui_ui_control","z":"667bf9ae.4b2b98","name":"","x":980,"y":300,"wires":[[]]},{"id":"aabdc9bc.4ad2d8","type":"ui_text","z":"667bf9ae.4b2b98","group":"de26add4.024cf","order":0,"width":"6","height":"2","name":"","label":"WORKING !!!!","format":"Wait Please","layout":"col-center","x":377,"y":222,"wires":[]},{"id":"a86217f1.1dca08","type":"dsm","z":"667bf9ae.4b2b98","name":"BlindControl","sm_config":"{\n    \"stateOutput\": \"state\",\n    \"currentState\": \"onHalt\",\n    \"states\": {\n        \"onHalt\": {\n            \"power\": \"active\"\n        },\n        \"active\": {\n            \"complRunAndCheck\": \"onHalt\"\n        }\n    },\n    \"data\": {\n        \"temp\": 0,\n        \"current\": 0,\n        \"UP\": 1,\n        \"DOWN\": 1,\n        \"runTime\": 0\n    },\n    \"methods\": {\n        \"temp\": {\n            \"name\": \"setData\"\n        },\n        \"current\": {\n            \"name\": \"setData\"\n        },\n        \"UP\": {\n            \"name\": \"setData\"\n        },\n        \"DOWN\": {\n            \"name\": \"setData\"\n        },\n        \"runTime\": {\n            \"name\": \"setData\"\n        },\n        \"setPoint\": [\n            \"sm.data.temp = msg.payload;\",\n            \"if (sm.currentState === 'active') {\",\n            \"   output = false;\",\n            \"} else {\",            \n            \"   if (msg.payload > sm.data.current) {\",\n            \"       sm.data.UP = 0 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       sm.direction = 1 ;\",\n            \"       sm.data.runTime = msg.payload - sm.data.current;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'green';\",\n            \"       sm.text = 'activeLift';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else if (msg.payload < sm.data.current) {\",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 0 ;\",\n            \"       sm.direction = -1 ;\",\n            \"       sm.data.runTime = sm.data.current - msg.payload;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'red';\",\n            \"       sm.text = 'activeLower';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else { \",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       output = true;\",\n            \"   } \",\n            \"} \"\n        ],\n        \"power\": [\n            \"timeout.endRum = setTimeout(function() {\",\n            \"   sm.data.UP = 1;\",\n            \"   sm.data.DOWN = 1;\",\n            \"   sm.fill = 'grey';\",\n            \"   sm.text = 'onHalt';\",\n            \"   resume('complRunAndCheck', msg);\",\n            \"}, sm.data.runTime);\",\n            \"output = false;\"\n            ],\n        \"complRunAndCheck\": [\n            \"if (sm.data.temp < sm.data.current && sm.direction === 1 || sm.data.temp > sm.data.current && sm.direction === -1) {\",\n            \"   timeout.changeDir = setTimeout(function() {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"   }, 150);\",\n            \"} else if (sm.data.temp != sm.data.current) {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"} else {\",\n            \"}\"\n            ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.fill\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"sm.text\"\n            }\n        }\n    }\n}","x":636,"y":205,"wires":[["8fa3535.0cd5db"]]},{"id":"2c8e9696.8d47ea","type":"inject","z":"667bf9ae.4b2b98","name":"init","topic":"setPoint","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":166,"y":172,"wires":[["7f1473c1.29567c","8b756896.072b78","c048381b.7710a8"]]},{"id":"7f1473c1.29567c","type":"function","z":"667bf9ae.4b2b98","name":"BlindProfile","func":"msg1={};\nmsg2={};\nmsg3={};\nmsg4={};\nmsg5={};\n// var unoProfile = { step:0, max:0 };\nmsg1.topic = \"temp\";\nmsg1.payload = 0;\nmsg2.topic = \"current\";\nmsg2.payload = 0;\nmsg3.topic = \"UP\";\nmsg3.payload = 1;\nmsg3.topic = \"DOWN\";\nmsg3.payload = 1;\nmsg2.topic = \"runTime\";\nmsg2.payload = 0;\nreturn [ msg1, msg2, msg3, msg4, msg5];","outputs":5,"noerr":0,"x":346,"y":292,"wires":[["a86217f1.1dca08"],["a86217f1.1dca08"],["a86217f1.1dca08"],["a86217f1.1dca08"],["a86217f1.1dca08"]]},{"id":"8fa3535.0cd5db","type":"function","z":"667bf9ae.4b2b98","name":"RpiPinDist","func":"msg1={};\nmsg2={};\nmsg3={};\nmsg4={};\nmsg1.payload = msg.data.UP;\nmsg2.payload = msg.data.DOWN;\nif (msg.data.UP && msg.data.DOWN) {\n    msg3.payload = \"hide\";\n    msg4.payload = \"display\";\n} else {\n    msg3.payload = \"display\";\n    msg4.payload = \"hide\";\n}\nreturn [ msg1, msg2, msg3, msg4 ];","outputs":4,"noerr":0,"x":626,"y":292,"wires":[["afcb2b93.4a87a8"],["69c22286.a3d2ec"],["4a4a64cb.a7e85c"],["1124b68a.7a4729"]]},{"id":"afcb2b93.4a87a8","type":"rpi-gpio out","z":"667bf9ae.4b2b98","name":"","pin":"32","set":true,"level":"1","freq":"","out":"out","x":896,"y":184,"wires":[],"inputLabels":["Window#1"]},{"id":"69c22286.a3d2ec","type":"rpi-gpio out","z":"667bf9ae.4b2b98","name":"","pin":"36","set":true,"level":"1","freq":"","out":"out","x":896,"y":232,"wires":[],"inputLabels":["Window#1"]},{"id":"c048381b.7710a8","type":"function","z":"667bf9ae.4b2b98","name":"adaptBlindOne","func":"const MAX = 27000\nmsg.payload = msg.payload*MAX/10;\nreturn msg;","outputs":1,"noerr":0,"x":516,"y":52,"wires":[["a86217f1.1dca08"]]},{"id":"8b756896.072b78","type":"ui_slider","z":"667bf9ae.4b2b98","name":"BlindHeightWidget","label":"{{ value }}","group":"26bd3a6a.d77a26","order":1,"width":"6","height":"1","passthru":true,"outs":"end","topic":"setPoint","min":0,"max":"10","step":"1","x":382,"y":172,"wires":[["c048381b.7710a8"]],"outputLabels":["setpoint"]},{"id":"4bd10fef.81206","type":"change","z":"667bf9ae.4b2b98","name":"SliderMgnt","rules":[{"t":"change","p":"payload","pt":"msg","from":"display","fromt":"str","to":"{\"group\":{\"show\":[\"TheBridgeBlinds_GO-TWO\"]}}","tot":"json"},{"t":"change","p":"payload","pt":"msg","from":"hide","fromt":"str","to":"{\"group\":{\"hide\":[\"TheBridgeBlinds_GO-TWO\"]}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":694,"wires":[["ddd67ff8.d8d39"]]},{"id":"2f28d9c2.ab69c6","type":"change","z":"667bf9ae.4b2b98","name":"MsgMgnt","rules":[{"t":"change","p":"payload","pt":"msg","from":"display","fromt":"str","to":"{\"group\":{\"show\":[\"TheBridgeBlinds_Msg2\"]}}","tot":"json"},{"t":"change","p":"payload","pt":"msg","from":"hide","fromt":"str","to":"{\"group\":{\"hide\":[\"TheBridgeBlinds_Msg2\"]}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":654,"wires":[["ddd67ff8.d8d39"]]},{"id":"ddd67ff8.d8d39","type":"ui_ui_control","z":"667bf9ae.4b2b98","name":"","x":980,"y":654,"wires":[[]]},{"id":"bed307df.167808","type":"ui_text","z":"667bf9ae.4b2b98","group":"91c8f5ad.bc1668","order":0,"width":"6","height":"2","name":"","label":"WORKING !!!!","format":"Wait Please","layout":"col-center","x":377,"y":576,"wires":[]},{"id":"61b44efc.4e5b","type":"dsm","z":"667bf9ae.4b2b98","name":"BlindControl","sm_config":"{\n    \"stateOutput\": \"state\",\n    \"currentState\": \"onHalt\",\n    \"states\": {\n        \"onHalt\": {\n            \"power\": \"active\"\n        },\n        \"active\": {\n            \"complRunAndCheck\": \"onHalt\"\n        }\n    },\n    \"data\": {\n        \"temp\": 0,\n        \"current\": 0,\n        \"UP\": 1,\n        \"DOWN\": 1,\n        \"runTime\": 0\n    },\n    \"methods\": {\n        \"temp\": {\n            \"name\": \"setData\"\n        },\n        \"current\": {\n            \"name\": \"setData\"\n        },\n        \"UP\": {\n            \"name\": \"setData\"\n        },\n        \"DOWN\": {\n            \"name\": \"setData\"\n        },\n        \"runTime\": {\n            \"name\": \"setData\"\n        },\n        \"setPoint\": [\n            \"sm.data.temp = msg.payload;\",\n            \"if (sm.currentState === 'active') {\",\n            \"   output = false;\",\n            \"} else {\",            \n            \"   if (msg.payload > sm.data.current) {\",\n            \"       sm.data.UP = 0 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       sm.direction = 1 ;\",\n            \"       sm.data.runTime = msg.payload - sm.data.current;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'green';\",\n            \"       sm.text = 'activeLift';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else if (msg.payload < sm.data.current) {\",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 0 ;\",\n            \"       sm.direction = -1 ;\",\n            \"       sm.data.runTime = sm.data.current - msg.payload;\",\n            \"       sm.data.current = msg.payload;\",\n            \"       sm.fill = 'red';\",\n            \"       sm.text = 'activeLower';\",\n            \"       resume('power', msg);\",\n            \"       output = true;\",\n            \"   } else { \",\n            \"       sm.data.UP = 1 ;\",\n            \"       sm.data.DOWN = 1 ;\",\n            \"       output = true;\",\n            \"   } \",\n            \"} \"\n        ],\n        \"power\": [\n            \"timeout.endRum = setTimeout(function() {\",\n            \"   sm.data.UP = 1;\",\n            \"   sm.data.DOWN = 1;\",\n            \"   sm.fill = 'grey';\",\n            \"   sm.text = 'onHalt';\",\n            \"   resume('complRunAndCheck', msg);\",\n            \"}, sm.data.runTime);\",\n            \"output = false;\"\n            ],\n        \"complRunAndCheck\": [\n            \"if (sm.data.temp < sm.data.current && sm.direction === 1 || sm.data.temp > sm.data.current && sm.direction === -1) {\",\n            \"   timeout.changeDir = setTimeout(function() {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"   }, 150);\",\n            \"} else if (sm.data.temp != sm.data.current) {\",\n            \"   msg.payload = sm.data.temp;\",\n            \"   resume('setPoint', msg);\",\n            \"   output = false;\",\n            \"} else {\",\n            \"}\"\n            ],\n        \"status\": {\n            \"fill\": {\n                \"get\": \"sm.fill\"\n            },\n            \"shape\": \"dot\",\n            \"text\": {\n                \"get\": \"sm.text\"\n            }\n        }\n    }\n}","x":636,"y":559,"wires":[["56ef231d.e7bb8c"]]},{"id":"c1755c3d.93246","type":"inject","z":"667bf9ae.4b2b98","name":"init","topic":"setPoint","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":166,"y":526,"wires":[["47c4c534.356f4c","9ed7d29c.f1a65","7383224f.161dfc"]]},{"id":"47c4c534.356f4c","type":"function","z":"667bf9ae.4b2b98","name":"BlindProfile","func":"msg1={};\nmsg2={};\nmsg3={};\nmsg4={};\nmsg5={};\n// var unoProfile = { step:0, max:0 };\nmsg1.topic = \"temp\";\nmsg1.payload = 0;\nmsg2.topic = \"current\";\nmsg2.payload = 0;\nmsg3.topic = \"UP\";\nmsg3.payload = 1;\nmsg3.topic = \"DOWN\";\nmsg3.payload = 1;\nmsg2.topic = \"runTime\";\nmsg2.payload = 0;\nreturn [ msg1, msg2, msg3, msg4, msg5];","outputs":5,"noerr":0,"x":346,"y":646,"wires":[["61b44efc.4e5b"],["61b44efc.4e5b"],["61b44efc.4e5b"],["61b44efc.4e5b"],["61b44efc.4e5b"]]},{"id":"56ef231d.e7bb8c","type":"function","z":"667bf9ae.4b2b98","name":"RpiPinDist","func":"msg1={};\nmsg2={};\nmsg3={};\nmsg4={};\nmsg1.payload = msg.data.UP;\nmsg2.payload = msg.data.DOWN;\nif (msg.data.UP && msg.data.DOWN) {\n    msg3.payload = \"hide\";\n    msg4.payload = \"display\";\n} else {\n    msg3.payload = \"display\";\n    msg4.payload = \"hide\";\n}\nreturn [ msg1, msg2, msg3, msg4 ];","outputs":4,"noerr":0,"x":626,"y":646,"wires":[["3e36161e.32238a"],["a6a1337c.24591"],["2f28d9c2.ab69c6"],["4bd10fef.81206"]]},{"id":"3e36161e.32238a","type":"rpi-gpio out","z":"667bf9ae.4b2b98","name":"","pin":"7","set":true,"level":"1","freq":"","out":"out","x":896,"y":538,"wires":[],"inputLabels":["Window#1"]},{"id":"a6a1337c.24591","type":"rpi-gpio out","z":"667bf9ae.4b2b98","name":"","pin":"11","set":true,"level":"1","freq":"","out":"out","x":896,"y":586,"wires":[],"inputLabels":["Window#1"]},{"id":"7383224f.161dfc","type":"function","z":"667bf9ae.4b2b98","name":"adaptBlindTwo","func":"const MAX = 19000\nmsg.payload = msg.payload*MAX/10;\nreturn msg;","outputs":1,"noerr":0,"x":516,"y":406,"wires":[["61b44efc.4e5b"]]},{"id":"9ed7d29c.f1a65","type":"ui_slider","z":"667bf9ae.4b2b98","name":"BlindHeightWidget","label":"{{ value }}","group":"78206801.db6798","order":1,"width":"6","height":"1","passthru":true,"outs":"end","topic":"setPoint","min":0,"max":"10","step":"1","x":382,"y":526,"wires":[["7383224f.161dfc"]],"outputLabels":["setpoint"]},{"id":"de26add4.024cf","type":"ui_group","z":"","name":"Msg1","tab":"7e3910f.9132af","disp":true,"width":"6","collapse":false},{"id":"26bd3a6a.d77a26","type":"ui_group","z":"","name":"GO-ONE","tab":"7e3910f.9132af","disp":true,"width":"6","collapse":false},{"id":"91c8f5ad.bc1668","type":"ui_group","z":"","name":"Msg2","tab":"7e3910f.9132af","disp":true,"width":"6","collapse":false},{"id":"78206801.db6798","type":"ui_group","z":"","name":"GO-TWO","tab":"7e3910f.9132af","disp":true,"width":"6","collapse":false},{"id":"7e3910f.9132af","type":"ui_tab","z":"","name":"TheBridgeBlinds","icon":"dashboard"}]
`

yes, the "only on release "mode definitely improves the behavior. Thank you!

Digging up an old topic here.

This works great, and I've learned a lot from it, so thanks!

One additional question: this function delays every incoming message, but is there a way to ignore any additional messages received during the delay period?

Example: an inject node fires a timestamp once per second, the function delays the first incoming timestamp by 5 seconds, and the 4 additional timestamps received during the first delay period would be discarded.

Currently, the function delays each additional timestamp by 5 seconds, so after 5 seconds, they arrive once per second as if the function wasn't doing anything.

Look at the delay node in rate limit mode

That would work beautifully, but Iā€™m trying to set the delay time to a value I have stored in global context.

Is it a value you set once ? Or is it dynamic ?

Ideally dynamic, as the system could adjust its own timing to compensate for additional tasks it receives from outside sources. Thatā€™s why Iā€™d like to use a function.

1 Like

Here's a description of the current project, for some context, if you're interested :slight_smile:

Let's me go back to the original question. I have a logic where I need to implement some delay. I have to do it in a way that I stay inside a loop of my existing logic.

I did this proof of concept code which should delay the output by msg.payload seconds.

The idea was that I create a while loop which loops until a counter reaches the designed msg.payload value. And within this loop I create a setTimeout function that increases this internal timer advancing it by 1 second each time:

let sleepcounter = 0;
let lastcounter = -1;

while (sleepcounter < msg.payload) {
    node.warn("While "+sleepcounter+"<"+msg.payload);
    if (lastcounter !== sleepcounter) {
        lastcounter = sleepcounter;
        setTimeout(function () {
            sleepcounter++;
        }, 1000);
        node.send({"payload": sleepcounter});
        node.status({ fill: "blue", shape: "ring", text: sleepcounter });
    }
}
return msg;

I must have done something terrible. I added a node.warn also a node.send and node.status so I can see what the code is doing, but it is doing nothing. And I think also crashing node-red.
The if statement was added so the code does not call setTimeout all the time, only until the timer actual expired and it is ready to be called again.

How come I am not seeing anything on the node status or the debugger?

Why?

Because the loop happens so fast and exits. IE it is not doing what you think it is.


Truth bomb: avoid loops in functions where possible (unless you are using fixed small sized loops where they will definitely exit and not crash the node runtime). The nodejs runtime is a single threaded event loop & running loops will block the event loop causing poor performance and other side effects.


If you explain what you are doing and what you are trying to achieve we can advise.

I am building a general purpose rule engine where I define various conditions (like if a sensor value is above a certain threshold) and if the conditions are met, there are a set of instructions to perform. Maybe turn on a lamp, wait, turn it off and also do something else.
I expect the code to do something after the delay (not just sending data) this is why I wanted to keep the processing within the loop.

I know my other option would be to send the data out on a second output and loop in back to the input through a delay node. And in that case I would also need to pass the entire context so the code can pick up the processing after the delay. Not impossible to do, but I was hoping to keep it simple and be able to delay the processing inside the function node.

For this kind of thing, a delay is not ideal IMO. An alternative to consider would be generating schedules (something like cron-plus) can have dynamically generated schedules that trigger when needed (you can also programmatically set the payload it emits)

As for your rule engine, if suggest making it from standard nodes in particular the link call in dynamic mode - which can be used to dynamically branch to areas of code based on some property value.

The most important part to remember node (and thus node-red) is primarily event based. Your solution should (ideally) be built with that in mind.

Thanks for these insight. I have done scheduling before, I think I can re-use that logic. Probably save the entire context for the schedule and change my code that it can return the execution after the delay step.
Should be doable.

There is nothing wrong with using a single setTimeout within a function as you can ensure you pass the correct context to the internal function. But looping is tricky (as you found) so generally best to avoid.

For completeness, the reason your code did not work, that the code

        lastcounter = sleepcounter;
        setTimeout(function () {
            sleepcounter++;
        }, 1000);
        node.send({"payload": sleepcounter});
        node.status({ fill: "blue", shape: "ring", text: sleepcounter });

starts the timer, but then immediately sends the message and the status. You would need to put the send and status calls inside the function to make them happen after 1 second. But you would also have to restructure the whole thing to get it to do what you want. Basically this technique is to be avoided if possible. It is tricky to get it right.

I wanted it to go out immediately, and the if statement was supposed to stop setTimeout to be started again if it is already running.
if (lastcounter !== sleepcounter) should only be true initially or after the timer function incremeneted a sleepcounter. In that case a timer will be triggered again...

Ok, I see what you are trying to do. The problem is that your code will sit, hogging the processor and stopping anything else going on, running round your loop at top speed waiting for the timer to run down. The result is that everything else going on in node red stops, so the dashboard will stop, no messages will get handled.

Maybe it's something you can use?

[{"id":"f16f1303aec8dfdb","type":"function","z":"da612d68cde527a4","name":"Timeout","func":"let msg1 = {}\nlet timeout = msg.payload // sec\n\nlet MyTimeOutID = context.get('MyTimeOutID') ?? undefined\n\nmsg1 = { payload: 'Done' }\n\nfunction MyTimeOut() {\n    if (MyTimeOutID != undefined) { clearTimeout(MyTimeOutID) }\n    MyTimeOutID = setTimeout(function () {\n        clearTimeout(MyTimeOutID)\n        node.send(msg1)\n    }, 1000 * timeout)\n}\n\nMyTimeOut()","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":1120,"wires":[["ef447c2c8430f28b"]]},{"id":"a10d62e7f60f8f58","type":"inject","z":"da612d68cde527a4","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":190,"y":1060,"wires":[["f16f1303aec8dfdb"]]},{"id":"b46aaa672e12435c","type":"inject","z":"da612d68cde527a4","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":190,"y":1100,"wires":[["f16f1303aec8dfdb"]]},{"id":"aabc209e36dcb703","type":"inject","z":"da612d68cde527a4","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"num","x":190,"y":1140,"wires":[["f16f1303aec8dfdb"]]},{"id":"d5109337a1d74ca7","type":"inject","z":"da612d68cde527a4","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"5","payloadType":"num","x":190,"y":1180,"wires":[["f16f1303aec8dfdb"]]},{"id":"ef447c2c8430f28b","type":"debug","z":"da612d68cde527a4","name":"debug 349","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":670,"y":1120,"wires":[]}]

1 Like

That can be done with a Delay node

image

[{"id":"bea323d7f318928b","type":"inject","z":"bdd7be38.d3b55","name":"0","props":[{"p":"payload"},{"p":"delay","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Done","payloadType":"str","x":150,"y":4220,"wires":[["0fc48874801a9618"]]},{"id":"7e19a24d12238d4a","type":"inject","z":"bdd7be38.d3b55","name":"1","props":[{"p":"payload"},{"p":"delay","v":"1000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Done","payloadType":"str","x":150,"y":4260,"wires":[["0fc48874801a9618"]]},{"id":"206b347015db8685","type":"inject","z":"bdd7be38.d3b55","name":"3","props":[{"p":"payload"},{"p":"delay","v":"3000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Done","payloadType":"str","x":150,"y":4300,"wires":[["0fc48874801a9618"]]},{"id":"de6dab64a58a2255","type":"inject","z":"bdd7be38.d3b55","name":"5","props":[{"p":"payload"},{"p":"delay","v":"5000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Done","payloadType":"str","x":150,"y":4340,"wires":[["0fc48874801a9618"]]},{"id":"0fc48874801a9618","type":"delay","z":"bdd7be38.d3b55","name":"Delay","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":370,"y":4280,"wires":[["cbe452e6f1189943"]]},{"id":"cbe452e6f1189943","type":"debug","z":"bdd7be38.d3b55","name":"debug 2478","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":580,"y":4280,"wires":[]}]

Thanks, I will check if that keeps the process running as mentioned above.