Resetting a timer with new input

Hi, I'm trying to make a timer using the function node in NodeRED. I've got the basics working, but I'm missing some key functionality that I can't get to work.

This is my flow:
image

I'm injecting number 5 in my function node. The timer starts running and outputs "finished" after 5 seconds. I'm changing the number 5 with a custom number from an input_slider from Home Assistant, so that I'm able to set a light turn off delay in seconds.

My timer will get an input every time my motion sensor detects someone. I'd like to extend the timer with another 5 seconds when that happens. With this timer, the first input results in a turn off call anyway, so it seems that my clearTimeout() function doesn't do its job properly. This is the code I'm using:

var payload = msg.payload;
var MyTimer;

function StopTimer() {
    clearTimeout(MyTimer);
    node.status({fill:"red", shape:"ring", text:' '});
}

function StartTimer() {
    MyTimer = setTimeout(function(){
        node.status({fill:"green", shape:"ring", text:" "});
        msg['payload'] = 'finished';
        node.send(msg);
    }, payload*1000);
}

StopTimer();
StartTimer();
node.status({fill:"blue", shape:"ring", text:' '});

I started with two functions. One to start the timer (StartTimer) and one to reset it (StopTimer). By stopping the previous timer, I'd expect the timer to extend the delay every time it gets input. But it does not.

Could someone help me?

Thanks in advance!

Roy

Have a look at Neil Cherry's MyTimeout Node - it will do what you want in the node rather than resorting to functions

Craig

1 Like

Assuming msg.payload is an integer coming from HA:

delay = parseInt(msg.payload*1000)
setTimeout(function(){
     msg.payload = 'finished';
    node.send(msg);
}, delay);

That's great! I was looking for such a node. I managed to get it working with the following flow:

[{"id":"b7724cd8.95304","type":"api-call-service","z":"444774fd.bb442c","name":"Overloop uit","server":"8d3f4965.cd7b58","version":1,"debugenabled":true,"service_domain":"homeassistant","service":"turn_off","entityId":"group.verlichting_overloop","data":"","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":2870,"y":1400,"wires":[[]],"inputLabels":["payload.data"]},{"id":"fcdeba97.bcb1a8","type":"api-current-state","z":"444774fd.bb442c","name":"Vertraging slapen?","server":"8d3f4965.cd7b58","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_number.verlichting_overloop_slapen_vertraging","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":1910,"y":1440,"wires":[["44f43ad.c7904c4","6132da91.8eed04"]]},{"id":"5f18fdb8.1f3524","type":"api-current-state","z":"444774fd.bb442c","name":"Vertraging thuis?","server":"8d3f4965.cd7b58","version":1,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_number.verlichting_overloop_thuis_vertraging","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":1910,"y":1360,"wires":[["44f43ad.c7904c4","81e2632c.6be0b"]]},{"id":"44f43ad.c7904c4","type":"change","z":"444774fd.bb442c","name":"Cancel timer","rules":[{"t":"set","p":"payload","pt":"msg","to":"cancel","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":2130,"y":1400,"wires":[["2c26ad51.639d02"]]},{"id":"2c26ad51.639d02","type":"mytimeout","z":"444774fd.bb442c","name":"Timer","outtopic":"","outsafe":"","outwarning":"","outunsafe":"stop","warning":"0","timer":"600","debug":false,"ndebug":false,"ignoreCase":false,"repeat":false,"again":false,"x":2690,"y":1400,"wires":[["b7724cd8.95304"],[]]},{"id":"81e2632c.6be0b","type":"delay","z":"444774fd.bb442c","name":"","pauseType":"delay","timeout":"10","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":2130,"y":1360,"wires":[["4eb16cc8.dfdc64"]]},{"id":"6132da91.8eed04","type":"delay","z":"444774fd.bb442c","name":"","pauseType":"delay","timeout":"10","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":2130,"y":1440,"wires":[["4eb16cc8.dfdc64"]]},{"id":"ae164646.58d598","type":"template","z":"444774fd.bb442c","name":"","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\"payload\":\"on\",\"timeout\":{{payload}},\"warning\":0}","output":"str","x":2520,"y":1360,"wires":[["2c26ad51.639d02"]]},{"id":"4eb16cc8.dfdc64","type":"function","z":"444774fd.bb442c","name":"Convert time","func":"var time = msg.payload*60;\nmsg['payload'] = time;\nnode.send(msg);\n    ","outputs":1,"noerr":0,"x":2330,"y":1360,"wires":[["ae164646.58d598"]]},{"id":"8d3f4965.cd7b58","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]

Thanks a lot!

no worries - glad to help - Neil is a frequent visitor on here - throw out a thanks to him as well

Craig

@ncherry Thank you! :smiley:

Your Welcome (thanks Colin) :blush:

Was looking how to do this via function. Was able to achieve it by utilizing flow variable (or global, up to u).
Basically, OP's code is spot on. It is just that he did not save the variable into flow context. Therefore a new variable is created everytime the function is called.

[{"id":"4810cf31.a718e","type":"inject","z":"da9fb98c.8e03e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":180,"wires":[["6b496c59.2ac0ec"]]},{"id":"6b496c59.2ac0ec","type":"change","z":"da9fb98c.8e03e8","name":"Set isDetecting","rules":[{"t":"set","p":"isDetecting","pt":"flow","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":180,"wires":[["c728974e.101138"]]},{"id":"dbfd5f7d.ade148","type":"change","z":"da9fb98c.8e03e8","name":"Set isDetecting","rules":[{"t":"set","p":"isDetecting","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":360,"wires":[["4546c516.834d1c"]]},{"id":"4546c516.834d1c","type":"debug","z":"da9fb98c.8e03e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":360,"y":420,"wires":[]},{"id":"c728974e.101138","type":"function","z":"da9fb98c.8e03e8","name":"","func":"var MyTimer = flow.get('detectTimer');\n\nfunction StopTimer() {\n    clearTimeout(MyTimer);\n}\n\nfunction StartTimer() {\n    MyTimer = setTimeout(function(){\n        msg['payload'] = 'finished';\n        node.send(msg);\n    }, 5000);\n    flow.set(\"detectTimer\", MyTimer)\n}\n\nStopTimer();\nStartTimer();","outputs":1,"noerr":0,"initialize":"","finalize":"","x":370,"y":280,"wires":[["dbfd5f7d.ade148"]]}]
1 Like

Yeah but much easier to use a visual node that has already been built and works well - remember it is a Visual/Low code programming environment !

Craig

Sorry about the thread lifting but I liked the original post with its code and simple timer without a lot of side effect features and I wanted to share my solution.

Above there are some comments about the problem with the original code. The value 'MyTimer' isn't saved between messages. It must be saved in the context. Node context is preferred so there will be possiblity to have multiple instances of the code in the same flow.
I made it so it will be possible to stop the timer with a value of 0 (and lower) and for all other values the timer is restarted with that time.
Some time in the future I will try to find out how to create my own node without all side. features..

Anyhow Here is my code (Note the my node send out a 1 when finish instead of the 'finished' string)

var delay = parseInt(msg.payload) * 1000;
var myTimer = context.get('myTimer');

function StopTimer() {
    if (myTimer != undefined) {
        clearTimeout(myTimer);
    }
    node.status({fill:"red", shape:"ring", text:'Idle'});
}

function StartTimer() {
    myTimer = setTimeout(function() {
        node.status({fill:"green", shape:"ring", text:'Done'});
        msg['payload'] = 1;
        node.send(msg);
    }, delay);
    node.status({fill:"blue", shape:"ring", text:'Wait'});
}

StopTimer();
if (delay > 0) StartTimer();

context.set('myTimer', myTimer);