Timer that can be extended on every push of a switch - current + X min?

Hi,
I tried to get help via home assistant community, but had no response, so I'm trying here now, even though I'm using NR inside HA and might use HA specific nodes...

I have a Node-red flow today to control an exhaust fan (while gluing, soldering etc) and it is connected to a NEXA (433MHz) plug controlled by a NEXA 3-channel remote and an RFXtrx to HA.
The remotes three channels is configured as three devices in HA:
ch1: direct control of the plug for the fan (on/off)
ch2: virtual - 30min timer
ch3: virtual - 4h timer
…then a complex NR flow that let me:
start continually by sending ch1=on
start 30min by hitting on for ch2
start 4h by hitting on for ch3
Repeat of ch2 or ch3 resets the timer and starts a new one.
Off switches everything off.

Now I’m working on reducing those 433MHz devices and plans to add an IKEA ZigBee plug and an IKEA ZigBee DimSwitch instead.

Trying to make a NR flow that lets me start 30min timer every time I send on by the IKEA DimSwitch and then add 30min for every additional on from the switch.

I started off with the node-red-contrib-stoptimer-varidelay node-red-contrib-stoptimer-varidelay (node) - Node-RED that can output the remaining time on third port and send that to a HA entity, so I should be able to display remaining time.

But how can I make it start on first press of the switch and then ADD 30min to current for every time I hit the switch? If it’s running and it’s 10 minutes left and I hit it twice I want 10+30+30 minutes…or If I want to start 2h I hit it 4 times…

…if that is possible, I need to figure out how to stop it by sending off and also reset the timer on off (to be able to stop and stop/restart on a shorter time).
I might have an idea for the later part, but I have tested, googled and can’t figure out how to increase (reset to current+X) the time of the running timer. My coding skills are quite…limited…I started on a function node and actually managed to add one variable to another(!). Maybe that could be a good start, , but when I thought I had something and tested, I had managed to create a never stopping loop that was counting and counting… :rofl:

I need some help…please…

1 Like

Hey John.

Anything's possible :wink:

Maybe what you should/could do it make a flow that just establishes what you want without all the external stuff.

That way other people can help you better.

I am NOT a HA person and anything that calls home is not of any interest to me. (Which I believe HA does.) But anyway.

Make a basic flow with buttons and a debug node to show what is being sent.
Get that working with the correct messages then all you do is connect the HA node.

Ok:
switch I'll read as button.

I have some ideas and what you want to do is not that difficult - in ways.

Multi-press buttons are not new and I have made a subflow to allow that.
I don't know if you like subflows.

I'll go away now and see if I can get something like what I understand you want and post back soon.

Oh: BIG QUESTION!

  1. How many buttons (switches) do you have?
  2. Are they basic buttons or do they send two messages when pressed and released?
  3. Are they ACTUAL buttons or part of Node-Red?

If 3: I'm out.

Hi,
I'm away at the moment and have only my phone. I'll try to make a start and post soon.

The button can probably be like payload = on... I want to use both s button in the HA app and an IKEA dim switch (zigbee) that sends payload = on when pressed (short click)

So, like payload = on -> current + X min

Example screen shots of what I have made.

If you press the button ONCE you get value 1 (10 seconds for testing)
If you press the button then soon after press it again: it adds 10 seconds to the time.

(above)
You can see the 10 seconds on/off times.

Now you see the button being pressed. A pause then pressed again before the 10 seconds.
(green lines)

You can see the output (blue lines)
You get 20 seconds.

I'll wait for you as I really need to know if this is doing what you want.

I found a few bugs in what I originally did.
(My fault/bad.)

Dig some more work and got this.

It has a subflow. Sorry.

I think it does a lot better job than the first effort.

There is a lot of debug stuff and test stuff.

And a LOT of READ ME nodes.

This is a FLOW/TAB.
Hope it helps.

[{"id":"872c2bc36b54739c","type":"function","z":"82a373101348c1cf","g":"c166709e9bc560ee","name":"time stamp message","func":"function addZero(i) {\n    if (i < 10) { i = \"0\" + i }\n    return i;\n}\nconst ts = new Date()\nlet hhmmss = addZero(ts.getHours()) + \":\" + addZero(ts.getMinutes()) + \":\" + addZero(ts.getSeconds())\nmsg.time = hhmmss\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":800,"y":480,"wires":[["6dae5536098bda13"]]}]

P.S

It still isn't perfect.

There is a problem with it but not insurmountable.

I will (probably) be bald but I can talk to you about it if this is really looking good/better.

thanks!

I will try to go through and understand the coming days! Will be traveling all day today and will do my best to keep away from it on the tiny phone screen so I don't mess something else up. Not too easy to handle NR on small touch screens, but difficult not to try as it's so fun to make functions when it starts working...
:grinning:

(If you do reply, please @ me so I get a notification.)
Otherwise it may get lost and I may not see your reply.

Hi agian,
I finally had some time to look at this again...but unfortunately I don't really understand...

My original Idea was to base it on the node stoptimer-varidelay.
The node stoptimer-varidelay accept input msg.delay=time and I want 30 min in final version.
It has three outputs; the first can send a stop signal, msg.payload=off
The third output can send remaining time

It's possible to stop it by sending msg.payload=stop for manual stop/reset.

But how do I make a function that takes the remaining time from third ouput and adds it to 30 min sent by an inject node to input

Something like this?

The inject node sends something like 30 min as input to start timer and the function node adds eventual remaining time=X from timer node third output, Y=30+X min...and sends it as msg.delay=Y

With this concept I think I would be able to use an IKEA zigbee dim switch that can send on and off and replace the inject node by a Home Assistant EventState-node and couple first output to a call-for-service-node to start my exhaust fan.
The Third output can then also be monitored in Home Assistant App to see remaining time

Have you really looked at the flow I posted?

The last bit is not done by the flow I posted. I admit. It NEARLY does it though.

You need to look at the function node I called track_times which I suspect does something like your function 1 node. Not sure.

I have spoken to the writer of the timer node and they are soon going to implement a needed output that would make what you want to do easy.
This was something I was wanting to talk to you about and would need some work from you as what WAS needed to be done is beyond me.
But as the writer of the node has told me:

I've now got this implemented locally. Will do some testing during the weekend and release it probably Sunday.

So I will have to wait a bit longer to see the release and then implement/use the new release to better control the run time.

The rest of my flow is to allow ONE button to do multiple presses to give you different times and TEST stuff so you can inject values directly just to see things happen at a base level.

Alas there is a second button to do the STOP function. But yours has that also.

The flow you posted would work, but there are problems with it.
If you change the time I don't see how it learns the new time to run.

I had to add extra nodes so when the time expires the run time is wiped.

Hi, this might solve your issue.

[{"id":"4c7b2d3f7a097ce2","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":570,"y":200,"wires":[["15efe483c517749b"]]},{"id":"c5d17846d72bb2d4","type":"stoptimer-varidelay","z":"27e9f4894231ed13","duration":"5","durationType":"num","units":"Second","payloadtype":"num","payloadval":"0","name":"","reporting":"every_second","persist":false,"ignoretimerpass":false,"x":1000,"y":200,"wires":[["0ba7b634c025f2c8"],["162f6badc056a5ee"],["7d593b16fcce88e5","d7433bffebd02e10"]]},{"id":"0ba7b634c025f2c8","type":"debug","z":"27e9f4894231ed13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1270,"y":180,"wires":[]},{"id":"162f6badc056a5ee","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1270,"y":220,"wires":[]},{"id":"7d593b16fcce88e5","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1270,"y":260,"wires":[]},{"id":"7875b6ad0ea12d0b","type":"function","z":"27e9f4894231ed13","name":"","func":"if (msg.payload == \"stop\") {\n    //stop everything\n    context.set(\"remaining\", 0);\n    node.send([msg, { payload: \"stopped\" }]);\n} else {\n    // does it match nn:nn:nn \n    let re = new RegExp(\"^([0-9]+):([0-9]+):([0-9]+)$\");\n    if (re.test(msg.payload)) {\n        let matches = re.exec(msg.payload);\n        let remainingSeconds = 3600 * Number(matches[1]) + Number(matches[2]) * 60 + Number(matches[3]);\n        context.set(\"remaining\", remainingSeconds);\n        // this is optional, set to false if no remaining seconds are required\n        if (true) {\n            msg.payload = remainingSeconds;\n            node.send([null, msg]);\n        }\n    } else {\n        // detect if payload is a number --> means create / increase timer\n        if (!isNaN(msg.payload)) {\n            //add seconds\n            let newmsg = {\"units\":\"Seconds\", payload:\"timer finished\"};\n            let remainingSeconds = context.get(\"remaining\") || 0;\n            remainingSeconds += msg.payload;\n            newmsg.delay = remainingSeconds;\n            node.send([newmsg, { payload: \"timer started with \" + remainingSeconds}]);\n        } else {\n            // handle something else?\n        }\n    }\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":200,"wires":[["bb9daf8218b121e5","c5d17846d72bb2d4"],["9fecf44e5962ed59"]]},{"id":"bb9daf8218b121e5","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":970,"y":140,"wires":[]},{"id":"9fecf44e5962ed59","type":"debug","z":"27e9f4894231ed13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":990,"y":280,"wires":[]},{"id":"bddb189d85f1aa16","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"30","payloadType":"num","x":570,"y":140,"wires":[["15efe483c517749b"]]},{"id":"c845737d3aae8e48","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":770,"y":160,"wires":[]},{"id":"98021780d7a8c209","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"num","x":570,"y":80,"wires":[["15efe483c517749b"]]},{"id":"15efe483c517749b","type":"junction","z":"27e9f4894231ed13","x":680,"y":200,"wires":[["7875b6ad0ea12d0b","c845737d3aae8e48"]]},{"id":"d7433bffebd02e10","type":"junction","z":"27e9f4894231ed13","x":760,"y":280,"wires":[["15efe483c517749b"]]}]
if (msg.payload == "stop") {
    //stop everything
    context.set("remaining", 0);
    node.send([msg, { payload: "stopped" }]);
} else {
    // does it match nn:nn:nn 
    let re = new RegExp("^([0-9]+):([0-9]+):([0-9]+)$");
    if (re.test(msg.payload)) {
        let matches = re.exec(msg.payload);
        let remainingSeconds = 3600 * Number(matches[1]) + Number(matches[2]) * 60 + Number(matches[3]);
        context.set("remaining", remainingSeconds);
        // this is optional, set to false if no remaining seconds are required
        if (true) {
            msg.payload = remainingSeconds;
            node.send([null, msg]);
        }
    } else {
        // detect if payload is a number --> means create / increase timer
        if (!isNaN(msg.payload)) {
            //add seconds
            let newmsg = {"units":"Seconds", payload:"timer finished"};
            let remainingSeconds = context.get("remaining") || 0;
            remainingSeconds += msg.payload;
            newmsg.delay = remainingSeconds;
            node.send([newmsg, { payload: "timer started with " + remainingSeconds}]);
        } else {
            // handle something else?
        }
    }
}

Any number N in payload starts the timer with N seconds or adds N seconds to the remaining time.
payload == "stop" ends the timer.

1 Like

Ah!

You did the magic I wasn't prepared to do.
I believe there is an update to the node about to happen that will better give the time remaining in actual seconds so it will save that magic you did. (Sorry.)

That is basically what I did if you look at my flow and a the Track times node.

I did a BASIC version of what you did.

But I also included a multipress button that allows you to use one button to send different values depending on how many times you press it.

YES! This was what I looked for.
And at least I understand a bit of the code in the function node. I will spend more time to try to understand the rest of it later.
Looks like I will be able to implement this together with an IKEA E1743 dim-swtich and a zigbee plugin!
Will get better than my 433MHz solution today!

I never realized that one of the bigger challenges was to handle that the node counts in minutes first and then i seconds.
For me, it would have been better to have it in minutes all the time, as I want to pass that over to HA dashboard and seconds gives such a high number. Maybe it's possible to add another function node on the 3rd output of the timer node that divides by 60 and round up to next biggest minute....later problem.

Big thanks Trying_to_learn and cameo69!!! :grinning:

Glad you like the solution.

To change the behaviour to minutes you only have to change one line of code:

change
remainingSeconds += msg.payload; to
remainingSeconds = Math.ceil(remainingSeconds / 60) * 60 + msg.payload *60;

Then every number you send to the function, will be handled as minutes and the remaining time will be rounded up to full minutes.

full function:

if (msg.payload == "stop") {
    //stop everything
    context.set("remaining", 0);
    node.send([msg, { payload: "stopped" }]);
} else {
    // does it match nn:nn:nn 
    let re = new RegExp("^([0-9]+):([0-9]+):([0-9]+)$");
    if (re.test(msg.payload)) {
        let matches = re.exec(msg.payload);
        let remainingSeconds = 3600 * Number(matches[1]) + Number(matches[2]) * 60 + Number(matches[3]);
        context.set("remaining", remainingSeconds);
        // this is optional, set to false if no remaining seconds are required
        if (true) {
            msg.payload = remainingSeconds;
            node.send([null, msg]);
        }
    } else {
        // detect if payload is a number --> means create / increase timer
        if (!isNaN(msg.payload)) {
            //add seconds
            let newmsg = {"units":"Seconds", payload:"timer finished"};
            let remainingSeconds = context.get("remaining") || 0;
            remainingSeconds = Math.ceil(remainingSeconds / 60) * 60 + msg.payload *60;
            newmsg.delay = remainingSeconds;
            node.send([newmsg, { payload: "timer started with " + remainingSeconds}]);
        } else {
            // handle something else?
        }
    }
}

Don't forget to mark it as solution :slight_smile:

No problems.

Alas I haven't seen an update to the node.
As I said: the person who made it said they would release a newer version that outputs the time remaining in seconds.
That would make getting the remaining time a lot easier.

Hi.

That node has been updated only recently. Like in the last few hours.

Here is a new flow which I was originally going to give you but for the problem with how it was.

As the node has been updated this is a nice way to do what you want.

[{"id":"4be6db179f2af0b9","type":"subflow","name":"Press count","info":"2023 01 17\n==========\nUsed to count button presses and send the number in a message.\n\nIf `msg.payload` == `reset` then `0` is sent out.\nHandy if you want to also include a *reset* ability.\n\nSet the *timeout* value in the `delay` variable.\n_This controls how long between presses before it stops counting._\n\nThen send the output message into a `switch` node to do wonderful things.\n\nThe output message is a `number`.","category":"","in":[{"x":60,"y":270,"wires":[{"id":"df8cb339f3aed3ba"}]}],"out":[{"x":870,"y":210,"wires":[{"id":"900cc4787c0480a4","port":0}]}],"env":[{"name":"delay","type":"num","value":"","ui":{"type":"input","opts":{"types":["num"]}}}],"meta":{},"color":"#D8BFD8","icon":"node-red-dashboard/ui_button.png","status":{"x":870,"y":370,"wires":[{"id":"900476fafffa3ae3","port":0}]}},{"id":"c2137fa8aec03468","type":"inject","z":"4be6db179f2af0b9","name":"Reset","repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":350,"y":290,"wires":[["5188ca7c92884164"]]},{"id":"de02a7b5bf7954d2","type":"trigger","z":"4be6db179f2af0b9","name":"","op1":"","op2":"0","op1type":"nul","op2type":"str","duration":"0.5","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":370,"y":250,"wires":[["a14dd5822f1f6015","900cc4787c0480a4"]]},{"id":"5188ca7c92884164","type":"change","z":"4be6db179f2af0b9","name":"Reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"foo","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":250,"wires":[["d5314c210f7d9a76"]]},{"id":"d5314c210f7d9a76","type":"counter","z":"4be6db179f2af0b9","name":"","init":"0","step":"1","lower":null,"upper":null,"mode":"increment","outputs":"1","x":520,"y":330,"wires":[["6a5f10e31345b897","bb0e17c295353204"]]},{"id":"6a5f10e31345b897","type":"change","z":"4be6db179f2af0b9","name":"Flow set","rules":[{"t":"set","p":"count","pt":"flow","to":"count","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":280,"wires":[[]]},{"id":"a457eafcdad07d0b","type":"change","z":"4be6db179f2af0b9","name":"","rules":[{"t":"set","p":"delay","pt":"msg","to":"delay","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":275,"y":330,"wires":[["d5314c210f7d9a76","de02a7b5bf7954d2"]],"l":false},{"id":"a14dd5822f1f6015","type":"change","z":"4be6db179f2af0b9","name":"","rules":[{"t":"delete","p":"delay","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":475,"y":250,"wires":[["5188ca7c92884164"]],"l":false},{"id":"900cc4787c0480a4","type":"function","z":"4be6db179f2af0b9","name":"","func":"msg.payload = flow.get(\"count\");\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":210,"wires":[[]]},{"id":"bb0e17c295353204","type":"function","z":"4be6db179f2af0b9","name":"CounterValue","func":"node.status({text: \"Counting \" + msg.count});\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":330,"wires":[[]]},{"id":"900476fafffa3ae3","type":"status","z":"4be6db179f2af0b9","name":"","scope":["bb0e17c295353204"],"x":720,"y":370,"wires":[[]]},{"id":"df8cb339f3aed3ba","type":"switch","z":"4be6db179f2af0b9","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"reset","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":150,"y":270,"wires":[["397e91b8f88a8919"],["a457eafcdad07d0b"]]},{"id":"397e91b8f88a8919","type":"delay","z":"4be6db179f2af0b9","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":360,"y":210,"wires":[["900cc4787c0480a4"]]},{"id":"e53afd60b8772d51","type":"subflow:4be6db179f2af0b9","z":"82a373101348c1cf","name":"","env":[{"name":"delay","value":"600","type":"num"}],"x":300,"y":290,"wires":[["51bdcaf00d997c52"]]},{"id":"f454a9dff8307281","type":"inject","z":"82a373101348c1cf","name":"Button","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"pressed","payloadType":"str","x":130,"y":290,"wires":[["e53afd60b8772d51"]]},{"id":"4e8236565044a985","type":"inject","z":"82a373101348c1cf","name":"Stop","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"reset","payloadType":"str","x":130,"y":340,"wires":[["e53afd60b8772d51"]]},{"id":"51bdcaf00d997c52","type":"switch","z":"82a373101348c1cf","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"}],"checkall":"true","repair":false,"outputs":5,"x":470,"y":290,"wires":[["12b64579a34e1e41"],["91dd50b368f8428f"],["98e07bfb9e5fbd76"],[],[]]},{"id":"12b64579a34e1e41","type":"change","z":"82a373101348c1cf","name":"Stop","rules":[{"t":"set","p":"payload","pt":"msg","to":"STOP","tot":"str"},{"t":"set","p":"reset","pt":"msg","to":"reset","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":190,"wires":[["8fcd70655a810791"]]},{"id":"91dd50b368f8428f","type":"change","z":"82a373101348c1cf","name":"10 seconds","rules":[{"t":"set","p":"delay","pt":"msg","to":"10","tot":"num"},{"t":"set","p":"value","pt":"msg","to":"10","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":260,"wires":[["2004f1a03d1872da"]]},{"id":"98e07bfb9e5fbd76","type":"change","z":"82a373101348c1cf","name":"20 seconds","rules":[{"t":"set","p":"delay","pt":"msg","to":"20","tot":"num"},{"t":"set","p":"value","pt":"msg","to":"20","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":300,"wires":[["2004f1a03d1872da"]]},{"id":"68c71ee7b2c7da5f","type":"change","z":"82a373101348c1cf","name":"30 minutes","rules":[{"t":"set","p":"delay","pt":"msg","to":"1800","tot":"num"},{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":90,"wires":[[]]},{"id":"7873795ee85d30bc","type":"change","z":"82a373101348c1cf","name":"60 minutes","rules":[{"t":"set","p":"delay","pt":"msg","to":"3600","tot":"num"},{"t":"set","p":"payload","pt":"msg","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":130,"wires":[[]]},{"id":"2ef1831e7490d880","type":"debug","z":"82a373101348c1cf","name":"ULTIMATE MESSAGE","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":540,"y":640,"wires":[]},{"id":"8fcd70655a810791","type":"function","z":"82a373101348c1cf","name":"Track times","func":"if (msg.payload == \"STOP\")\n{\n    flow.set(\"flow_delay\",0);\n    return msg;\n}\n\nlet flow_delay = flow.get(\"flow_delay\") || 0;\n\nlet new_delay = msg.delay + flow_delay;\n\nmsg.delay = new_delay;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":280,"wires":[["074528ccb447397b"]]},{"id":"8672fa47310f28d7","type":"switch","z":"82a373101348c1cf","name":"== OFF","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"OFF","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":710,"y":410,"wires":[["ddc8ace2f59a6bfd"]]},{"id":"ddc8ace2f59a6bfd","type":"change","z":"82a373101348c1cf","name":"STOP","rules":[{"t":"set","p":"payload","pt":"msg","to":"STOP","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":700,"y":370,"wires":[["8fcd70655a810791"]]},{"id":"081ac0d5f0974c32","type":"comment","z":"82a373101348c1cf","name":"Read me *","info":"These are the nodes you want.\nYou will need to make more of them to\nget 1.5 hours and 2 hours.\nThe values are in MINUTES!\n\nLook at the example nodes used\nand adjust as needed.\n\nmsg.value is NOT needed in the \nbigger picture.","x":480,"y":110,"wires":[]},{"id":"cd62a53efd514ce3","type":"comment","z":"82a373101348c1cf","name":"Read me *","info":"Press the `Button` as many times as you want.\nBut you will need to add more outputs\nto the `switch` node if more than `4`\n\n1 = 30 mins.\n2 = 60 mins.\n3 = 90 mins.\n4 = 2 hours.\n(etc)\n\n`Stop` stops all things.","x":130,"y":250,"wires":[]},{"id":"59db4e0712dee19d","type":"comment","z":"82a373101348c1cf","name":"Read me *","info":"This is set for `4` options/presses\nof the button.\nAdd more if needed.","x":470,"y":230,"wires":[]},{"id":"7f4d7544efd87231","type":"comment","z":"82a373101348c1cf","name":"Read me *","info":"Open the node below - if needed - and\nadjust the `delay` value to control how\nfast the clicking is/needs to be.\n","x":300,"y":250,"wires":[]},{"id":"c4840f7ff405a39d","type":"comment","z":"82a373101348c1cf","name":"Example values","info":"","x":630,"y":230,"wires":[]},{"id":"dedb794e18f92543","type":"debug","z":"82a373101348c1cf","name":"Remaining","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1130,"y":360,"wires":[]},{"id":"89497fb4a9ab412a","type":"function","z":"82a373101348c1cf","name":"remaining time.","func":"flow.set(\"flow_delay\",msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":890,"y":420,"wires":[[]]},{"id":"074528ccb447397b","type":"stoptimer-varidelay","z":"82a373101348c1cf","duration":"5","durationType":"num","units":"Second","payloadtype":"str","payloadval":"OFF","name":"","reporting":"every_second","reportingformat":"seconds","persist":false,"ignoretimerpass":false,"x":500,"y":410,"wires":[[],["8672fa47310f28d7","422f74f18758af19"],["dedb794e18f92543","89497fb4a9ab412a"]]},{"id":"2004f1a03d1872da","type":"junction","z":"82a373101348c1cf","x":730,"y":280,"wires":[["fbecd3ec9736dc0e","8fcd70655a810791","422f74f18758af19"]]},{"id":"422f74f18758af19","type":"junction","z":"82a373101348c1cf","x":650,"y":480,"wires":[["872c2bc36b54739c"]]},{"id":"c166709e9bc560ee","type":"group","z":"82a373101348c1cf","name":"Not really needed","style":{"stroke":"#ff3f3f","label":true,"color":"#ff0000"},"nodes":["6dae5536098bda13","872c2bc36b54739c"],"x":674,"y":439,"w":252,"h":122},{"id":"6dae5536098bda13","type":"template","z":"82a373101348c1cf","g":"c166709e9bc560ee","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{time}}  -- {{payload}}","output":"str","x":760,"y":520,"wires":[["2ef1831e7490d880"]]},{"id":"872c2bc36b54739c","type":"function","z":"82a373101348c1cf","g":"c166709e9bc560ee","name":"time stamp message","func":"function addZero(i) {\n    if (i < 10) { i = \"0\" + i }\n    return i;\n}\nconst ts = new Date()\nlet hhmmss = addZero(ts.getHours()) + \":\" + addZero(ts.getMinutes()) + \":\" + addZero(ts.getSeconds())\nmsg.time = hhmmss\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":800,"y":480,"wires":[["6dae5536098bda13"]]},{"id":"9befc6bf50c35625","type":"group","z":"82a373101348c1cf","name":"Not really needed","style":{"stroke":"#ff3f3f","label":true,"color":"#ff0000"},"nodes":["fbecd3ec9736dc0e","70ec6c89f1ce040e"],"x":784,"y":119,"w":232,"h":122},{"id":"fbecd3ec9736dc0e","type":"template","z":"82a373101348c1cf","g":"9befc6bf50c35625","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{value}} -set- {{payload}}","output":"str","x":870,"y":160,"wires":[["70ec6c89f1ce040e"]]},{"id":"70ec6c89f1ce040e","type":"debug","z":"82a373101348c1cf","g":"9befc6bf50c35625","name":"middle message","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":890,"y":200,"wires":[]},{"id":"08f354719764dbaa","type":"group","z":"82a373101348c1cf","name":"TESTING","style":{"stroke":"#ff0000","label":true,"color":"#ff0000"},"nodes":["2ddd91ac7b55cf65","c6d4379949a68c85","b40be8c9d7fff916"],"x":54,"y":389,"w":172,"h":192},{"id":"2ddd91ac7b55cf65","type":"inject","z":"82a373101348c1cf","g":"08f354719764dbaa","name":"10 secs","props":[{"p":"delay","v":"70","vt":"num"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"ON","payloadType":"str","x":150,"y":480,"wires":[["074528ccb447397b","422f74f18758af19"]]},{"id":"c6d4379949a68c85","type":"inject","z":"82a373101348c1cf","g":"08f354719764dbaa","name":"14 secs","props":[{"p":"delay","v":"14","vt":"num"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"ON","payloadType":"str","x":150,"y":540,"wires":[["074528ccb447397b","422f74f18758af19"]]},{"id":"b40be8c9d7fff916","type":"inject","z":"82a373101348c1cf","g":"08f354719764dbaa","name":"STOP","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":150,"y":430,"wires":[["074528ccb447397b"]]}]

Don't forget to update the node!

Importing this may do that. But when I updated it, I had to RESTART Node-red to get it to work.

Hi and tanks!
I might have been unclear in my question but this was the solution to one of two questions which I forgot to ask! :slight_smile:
This makes it more straight forward to send a suitable time interval to the timer (even though it had been quite simple to calculate the number of seconds to send)
But is there such an elegant way to present convert what comes out from timer 3rd output to just minutes all the way down to 0...rounded up to next full minute, so it shows 1 until it shuts off and only have it to update on change?
I plan to publish remaining time to HA dashboard and have a button there as well to increase the timer when needed.

Or is that complicated. I wish I had more code experience when I come up with a lot of ideas I want to do...but at least I learn a little bit from every project I get help with...

EDIT: After starting to adapt it to my (coming) new hardware, I realized that both hours and minutes for the sensor I create from NR and publish in HA is useful, so I think it's perfectly fine as it is! Thanks again!

wow...a lot of functions! Need to look through more when I have more time...some late evening when the family have gone to sleep :grin:. Those complex solutions triggers learning :slight_smile: this is really great fun
I just wish I had more coding experience...

Quickly changed the whole flow to minutes.

If you would like to get the remaining minutes every second, just remove the rbe node.

[{"id":"76a320f6cc47047b","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"stop","payloadType":"str","x":530,"y":600,"wires":[["1f2942ff6f8b09b2"]]},{"id":"ad35387024569f31","type":"stoptimer-varidelay","z":"27e9f4894231ed13","duration":"5","durationType":"num","units":"Second","payloadtype":"num","payloadval":"0","name":"","reporting":"every_second","persist":false,"ignoretimerpass":false,"x":960,"y":600,"wires":[["f5f7118b53e9fcf9"],["2da546a78c09fe4a"],["58a7dbe1ea8bb2c0","ddb96336805b3416"]]},{"id":"f5f7118b53e9fcf9","type":"debug","z":"27e9f4894231ed13","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":580,"wires":[]},{"id":"2da546a78c09fe4a","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":620,"wires":[]},{"id":"58a7dbe1ea8bb2c0","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":660,"wires":[]},{"id":"93327c7a8c54828f","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":930,"y":540,"wires":[]},{"id":"2dc3f36104b6e464","type":"debug","z":"27e9f4894231ed13","name":"remaining minutes","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1250,"y":720,"wires":[]},{"id":"aba3d321d9545696","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"30","payloadType":"num","x":530,"y":540,"wires":[["1f2942ff6f8b09b2"]]},{"id":"d437b13c9745b1bc","type":"debug","z":"27e9f4894231ed13","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":730,"y":560,"wires":[]},{"id":"f67668c05b7d5e73","type":"inject","z":"27e9f4894231ed13","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"3","payloadType":"num","x":530,"y":480,"wires":[["1f2942ff6f8b09b2"]]},{"id":"9ff96c020d275d53","type":"function","z":"27e9f4894231ed13","name":"minutes","func":"if (msg.payload == \"stop\") {\n    //stop everything\n    context.set(\"remaining\", 0);\n    node.send([msg, { payload: \"stopped\" }]);\n} else {\n    // does it match nn:nn:nn \n    let re = new RegExp(\"^([0-9]+):([0-9]+):([0-9]+)$\");\n    if (re.test(msg.payload)) {\n        let matches = re.exec(msg.payload);\n        let remainingMinutes = 60 * Number(matches[1]) + Number(matches[2]) + 1;\n        context.set(\"remaining\", remainingMinutes);\n        msg.payload = remainingMinutes;\n        return [null, msg];\n    } else {\n        // detect if payload is a number --> means create / increase timer\n        if (!isNaN(msg.payload)) {\n            //add msg.payload as minutes\n            let newmsg = {\"units\":\"Minutes\", payload:\"timer finished\"};\n            let remainingMinutes = context.get(\"remaining\") || 0;\n            remainingMinutes += msg.payload;\n            newmsg.delay = remainingMinutes;\n            msg.payload = \"timer started with \" + remainingMinutes;\n            return [newmsg, msg];\n        }\n    }\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":740,"y":600,"wires":[["93327c7a8c54828f","ad35387024569f31"],["4b9d3cc979b05b28"]]},{"id":"4b9d3cc979b05b28","type":"rbe","z":"27e9f4894231ed13","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":930,"y":720,"wires":[["2dc3f36104b6e464"]]},{"id":"1f2942ff6f8b09b2","type":"junction","z":"27e9f4894231ed13","x":640,"y":600,"wires":[["d437b13c9745b1bc","9ff96c020d275d53"]]},{"id":"ddb96336805b3416","type":"junction","z":"27e9f4894231ed13","x":720,"y":680,"wires":[["1f2942ff6f8b09b2"]]}]

tested a bit more and realized that this minutes based version rounds up to next 30 min when I add extra 30 min...so if there is 5 minutes left and I inject 30 it becomes 60...not a dealbreaker, but I don't understand why...

Edit: It looks to be only when I press the HA button I created in NR (that publishes in HA) that it does like that. Inject node just adds 30 min to current!
Strange...doesn't really matter for what I plan to use it for, but I don't understand why.

(not had time to look at latest version with the rbe-node yet)

Edit:
Tried some more and if I inject more time directly, it goes to even 30 minutes even though a few seconds have passed. If I ad more time after waiting a while it just adds to current! But I can't figure out what in the code does like that...and now I get curious and want to learn :slight_smile:

Only 3.

Unless you are talking about the subflows also.

If you want to change the times to be extended you either edit the change nodes or use the extra two I supplied marked 30 minutes and 60 minutes.
You can add more if you want more button presses.
This will also mean you have to edit the switch node and add the extra outputs for the extra button press counts.

Sorry. I am not trying to steal anyone's thunder. But all the other flows posted don't allow multiple button presses to get you different values to be added.

That part is not easy to do - IMO - and to not confuse you I did all that in a subflow.