Pull down menu to select delay?

I am very new at node-red, So I still have to learn a lot.
The topics that touch on the subject I don't really understand.

What I am trying to create:
First select time on a pull down menu on the dashboard with options to select from 1 to 10 hours value.
Push button to activate.
First select a time then push the button. the relay connected to the pi turns on.
Depending on the time selected, wait that amount of time, then turn the relay off.
(if possible a countdown on the dashboard, but less important)

Problem I am facing is that the time on a delay node seems to be fixed in the node. I dont know how to inject my selected time from the pull down menu. I also get the feeling I am missing some nodes to get this to work. Could someone help me out? A picture on how this is supposed to look like would be very helpful.
thank you

Not good pratice to just power off a Pi, issue a 'sudo shutdown' (via an exec node), wait a minute, then shut off the relay.

have you checked ou the flow library to see if there is another node you could use?

I think there is a relay connected to the gpio and that gets turned on or off.

You have to change the fixed delay setting to override with msg.delay in the delay nodes configuration. You can than change the delay by accompanying the input message with the amount of milliseconds it should be delayed in msg.delay.

You could also take a look at:


alternatively if you want only core nodes I have a collection of subflows which can be used in the above way:




O, sorry for not explaining clearly. The relay board is a 4 relay board that controles my doorbell, monitor power, fan power controle. (+ option to reset the pi power after i did a safe shutdown)

Main thing I am focusing on is to turn my doorbell off for x amount of time and then switching it back on.

1 Like


so this is the mess I made so far.
i dont know how to change the output from the selection menu to output msg.delay instead of payload.

(to avoid confusion payload 1 turns the doorbell off and 0 turns it back on. the pi node is not connected because I am just testing)

@JGKK mentioned this already above, but I just wanted to point out that this is, besides the push button, exactly what the countdown-timer-switch does. Do you need to activate the timer via a button?

installed the node-red-contrib-ui-countdown-timer-switch. Yes a button to trigger after I selected the time would be nice.

Here is a very minimal example using my timers subflow and a dashboard form should you feel adventurous and diy:

[{"id":"d3399d9e.0ff088","type":"subflow","name":"timers","info":"This subflow/node facilitates the setting\nof multiple timers in an easy manner.\n\n# Input Message to set timer\n\nThe subflow expects an input `msg` which\ncontains the following to set a timer:\n\n+ a `msg.payload` which will be send once the timer has finished\n+ a `msg.duration` either in the format of a number in seconds or a string in the \"hh:mm:ss\" format\n+ an optional `msg.topic` which will also be send on completion\n\n# Other Control Messages\n\nThere are also a number of other control\nmessages which the subflow accept:\n\n+ a `msg.payload` of `reset` will reset all running timers\n+ a `msg.payload` of `debug` will send an array of all set timers to the secon debug output.\n+ a `msg` with a duration in seconds as a number or a string in the hh:mm:ss format with `msg.delete` set to true will delete the first set timer with a matching duration\n\n# Configuration Settings\n\nThe subflow although offers several menu options which can be adjusted:\n\n+ there is a setting which determines if a new timer should be appended to the list of existing timers and run in parallel or if every new timer should replace the previous\n+ a setting for the output format of the duration and the time left to-go which affects both the status display and the debug messages\n+ a setting to enable/disable the progress status being shown on the node status\n+ a setting which when enabled sends a debug message every second to the second output\n\n# Debug Message Format\nThe node sends a debug message to its second output on several events or in addition every second if configured. Those events are:\n\n+ When a new timer is set\n+ When a timer completes\n+ When a timer is deleted\n+ When the timers are reset\n\nThe format of this message is an array which\nincludes one object for every timer running\nthat includes useful information about each\ntimer in this format:\n```\n{\n    \"payload\":\"ON\",\n    \"duration\":\"00:00:25\",\n    \"due\":1607330155654,\n    \"toGo\":\"00:00:25\",\n    \"topic\":\"Test\"\n}\n```\nthe `toGo` and `duration` properties will be in the format configured in the nodes settings.","category":"","in":[{"x":100,"y":140,"wires":[{"id":"fe62abda.f8f7a"}]}],"out":[{"x":840,"y":140,"wires":[{"id":"c50c6881.b2fdf","port":0}]},{"x":840,"y":200,"wires":[{"id":"12c839f6.01eed6","port":0},{"id":"595b735c.08ff54","port":1}]}],"env":[{"name":"on_input","type":"str","value":"append","ui":{"label":{"en-US":"On new input"},"type":"select","opts":{"opts":[{"l":{"en-US":"append to list of current timers"},"v":"append"},{"l":{"en-US":"replace current timer"},"v":"replace"}]}}},{"name":"time_format","type":"str","value":"hhmmss","ui":{"label":{"en-US":"Time format of output/status"},"type":"select","opts":{"opts":[{"l":{"en-US":"hh:mm:ss"},"v":"hhmmss"},{"l":{"en-US":"seconds"},"v":"seconds"}]}}},{"name":"show_progress","type":"bool","value":"true","ui":{"label":{"en-US":"show progress in status"},"type":"checkbox"}},{"name":"send_progress","type":"bool","value":"false","ui":{"label":{"en-US":"send progress to second output"},"type":"checkbox"}}],"color":"#FDF0C2","icon":"node-red/timer.svg","status":{"x":840,"y":80,"wires":[{"id":"415b9d3f.6e5754","port":0}]}},{"id":"fe326ef6.0184b","type":"function","z":"d3399d9e.0ff088","name":"tick","func":"const timers = flow.get(\"timers\") || [];\nif (msg.delete === true || timers.length > 0) {\n    return msg;\n}\nconst start = Date.now();\nconst startCorrected = (Math.ceil(start / 1000)) * 1000;\nfunction initial() {\n    initialTimeout = startCorrected - start;\n    setTimeout(()=>{\n        tick(1000);\n        node.send({payload:\"tick\"});\n    },initialTimeout);\n}\nfunction tick(timeout) {\n    setTimeout(()=>{\n        if (flow.get(\"timers\").length > 0) {\n            const newStart = Date.now();\n            const offset = (newStart-startCorrected) % 10;\n            const newTimeout = (offset > 0) ? 1000-(offset) : 1000;\n            tick(newTimeout);\n            node.send({payload:\"tick\"});\n        }\n    },timeout)\n}\ninitial();\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":550,"y":140,"wires":[["c50c6881.b2fdf","415b9d3f.6e5754"]]},{"id":"c50c6881.b2fdf","type":"function","z":"d3399d9e.0ff088","name":"check timers","func":"const now = Date.now();\nlet timers = flow.get(\"timers\") || [];\nif (msg.payload === \"tick\") {\n    if (timers.length === 0) { return null; } \n    let done = [];\n    timers.forEach(timer => {\n        if (timer.due < now) {\n            let outputMsg = {};\n            outputMsg.payload = timer.payload;\n            if (timer.hasOwnProperty(\"topic\")) { outputMsg.topic = timer.topic }\n            node.send([outputMsg, null]);\n            done.push(timer);\n        }\n    });\n    if (done.length !== 0) {\n        done.forEach(item => {\n            let toDelete = timers.indexOf(item);\n            timers.splice(toDelete, 1);\n        });\n        node.send([null,{payload:timers}]);\n    }\n    timers.forEach((timer, index) => {\n        timers[index].toGo = Math.round((timer.due - now) / 1000);\n    });\n    if (env.get(\"send_progress\") === true && done.length === 0 ) {\n        node.send([null,{payload:timers}]);\n    }\n} else if (msg.delete) {\n    for(i=0;i<timers.length;i++) {\n        if (timers[i].duration === msg.payload) {\n            timers.splice(i, 1);\n            node.send([null,{payload:timers}]);\n            break;\n        }\n    }\n} else {\n    let newTimer = {\n        payload: msg.payload,\n        duration: msg.duration,\n        due: now + (msg.duration * 1000),\n        toGo: msg.duration\n    };\n    if (msg.hasOwnProperty(\"topic\")) { newTimer.topic = msg.topic }\n    if (env.get(\"on_input\") === \"append\") {\n        timers.push(newTimer);\n    } else {\n        timers = [newTimer];\n    }\n    node.send([null,{payload:timers}]);\n}\nflow.set(\"timers\",timers);\nreturn null;","outputs":2,"noerr":0,"initialize":"","finalize":"","x":710,"y":140,"wires":[[],["12c839f6.01eed6"]]},{"id":"415b9d3f.6e5754","type":"function","z":"d3399d9e.0ff088","name":"set status","func":"function convertFormat(input){\n    let hoursRaw = input / 3600;\n    hoursRaw = hoursRaw.toString().split(\".\");\n    let hours = hoursRaw[0];\n    let minutesRaw = (input / 60) - (Number(hours) * 60);\n    minutesRaw = minutesRaw.toString().split(\".\");\n    let minutes = minutesRaw[0];\n    let seconds = (input - (Number(hours) * 3600) - (Number(minutes) * 60)).toString();\n    if (hours.length === 1) { hours = \"0\" + hours; }\n    if (minutes.length === 1) { minutes = \"0\" + minutes; }\n    if (seconds.length === 1) { seconds = \"0\" + seconds; }\n    const output = `${hours}:${minutes}:${seconds}`;\n    return output;\n}\nconst timers = flow.get(\"timers\") || [];\nif (env.get(\"show_progress\") === true) {\n    if (timers.length !== 0) {\n        const now = Date.now();\n        let newPayload = \"\";\n        timers.forEach(timer => {\n            let toGo = Math.round((timer.due - now) / 1000);\n            let addPayload = \"\";\n            if (env.get(\"time_format\") === \"seconds\") {\n                addPayload = `${toGo}s of ${timer.duration}s to go`;\n            } else {\n                let newDuration = convertFormat(timer.duration);\n                let newToGo = convertFormat(toGo);\n                addPayload = `${newToGo} of ${newDuration} to go`;\n            }\n            if (newPayload.length !== 0) { newPayload += \", \"; }\n            newPayload += addPayload;\n        });\n        node.send({payload:newPayload});\n    } else {\n        node.send({payload:\"no timer\"});\n    }\n}\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":700,"y":80,"wires":[[]]},{"id":"fe62abda.f8f7a","type":"switch","z":"d3399d9e.0ff088","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"reset","vt":"str"},{"t":"eq","v":"debug","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":210,"y":140,"wires":[["1e5213c7.d9c46c"],["1461b44c.50eae4"],["595b735c.08ff54"]]},{"id":"1e5213c7.d9c46c","type":"change","z":"d3399d9e.0ff088","name":"reset","rules":[{"t":"set","p":"timers","pt":"flow","to":"[]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":80,"wires":[["415b9d3f.6e5754","1461b44c.50eae4"]]},{"id":"595b735c.08ff54","type":"function","z":"d3399d9e.0ff088","name":"error checking","func":"let newMsg = {};\nif (typeof msg.duration === \"string\" && msg.duration.match(/[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}/g)) {\n    msg.duration = msg.duration.split(\":\");\n    msg.duration = (Number(msg.duration[0]) * 3600) + (Number(msg.duration[1]) * 60) + Number(msg.duration[2]);\n}\nif (msg.delete === true && typeof msg.payload === \"string\" && msg.payload.match(/[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}/g)) {\n    msg.payload = msg.payload.split(\":\");\n    msg.payload = (Number(msg.payload[0]) * 3600) + (Number(msg.payload[1]) * 60) + Number(msg.payload[2]);\n}\nif (msg.delete !== true) {\n    if (msg.duration === undefined || typeof msg.duration !== \"number\" || msg.duration <= 0) {\n        newMsg.payload = \"input message needs a msg.duration property which is either of type number or a string in the hh:mm:ss second format\";\n        node.warn(newMsg.payload);\n        return [null, newMsg];\n    } else if (msg.payload === undefined) {\n        newMsg.payload = \"msg.payload needs to be defined\";\n        node.warn(newMsg.payload);\n        return [null, newMsg];\n    }\n} else if (typeof msg.payload !== \"number\" || msg.payload <= 0) {\n    newMsg.payload - \"if msg.delete is true msg.payload needs to be of type number and coresponding to the duration of the timer to be deleted\";\n    node.warn(newMsg.payload);\n    return [null, newMsg];\n}\nreturn [msg, null];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":380,"y":140,"wires":[["fe326ef6.0184b"],[]]},{"id":"12c839f6.01eed6","type":"function","z":"d3399d9e.0ff088","name":"format output","func":"function convertFormat(input){\n    let hoursRaw = input / 3600;\n    hoursRaw = hoursRaw.toString().split(\".\");\n    let hours = hoursRaw[0];\n    let minutesRaw = (input / 60) - (Number(hours) * 60);\n    minutesRaw = minutesRaw.toString().split(\".\");\n    let minutes = minutesRaw[0];\n    let seconds = (input - (Number(hours) * 3600) - (Number(minutes) * 60)).toString();\n    if (hours.length === 1) { hours = \"0\" + hours; }\n    if (minutes.length === 1) { minutes = \"0\" + minutes; }\n    if (seconds.length === 1) { seconds = \"0\" + seconds; }\n    const output = `${hours}:${minutes}:${seconds}`;\n    return output;\n}\nif(env.get(\"time_format\") === \"hhmmss\") {\n    let newMsg = RED.util.cloneMessage(msg);\n    newMsg.payload.forEach(timer => {\n        timer.duration = convertFormat(timer.duration);\n        timer.toGo = convertFormat(timer.toGo);\n    });\n    return newMsg;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":700,"y":200,"wires":[[]]},{"id":"8370617b.6272a","type":"inject","z":"d3399d9e.0ff088","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":170,"y":80,"wires":[["1e5213c7.d9c46c"]]},{"id":"1461b44c.50eae4","type":"change","z":"d3399d9e.0ff088","name":"timers","rules":[{"t":"set","p":"payload","pt":"msg","to":"timers","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":200,"wires":[["12c839f6.01eed6"]]},{"id":"61cff8ff.3fe9b","type":"subflow:d3399d9e.0ff088","z":"7456984c.3829b8","name":"","env":[{"name":"on_input","value":"replace","type":"str"},{"name":"send_progress","type":"bool","value":"true"}],"x":530,"y":200,"wires":[["fbc17448.7067f"],["138a8f18.672b59"]]},{"id":"54c44b12.eae0ec","type":"ui_form","z":"7456984c.3829b8","name":"set doorbell timer","label":"Turn off doorbell for:","group":"d7c34dd1.1e1a78","order":1,"width":0,"height":0,"options":[{"label":"","value":"duration","type":"text","required":true,"rows":null}],"formValue":{"duration":""},"payload":"","submit":"turn off now","cancel":"","topic":"","x":170,"y":200,"wires":[["c0a68e65.1e33a8"]]},{"id":"c0a68e65.1e33a8","type":"change","z":"7456984c.3829b8","name":"doorbell off","rules":[{"t":"set","p":"duration","pt":"msg","to":"payload.duration","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":200,"wires":[["61cff8ff.3fe9b","36f8b574.b25cfa"]]},{"id":"fbc17448.7067f","type":"change","z":"7456984c.3829b8","name":"doorbell on","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":690,"y":200,"wires":[["36f8b574.b25cfa"]]},{"id":"9b0e82cc.7e52c","type":"ui_text","z":"7456984c.3829b8","group":"d7c34dd1.1e1a78","order":2,"width":0,"height":0,"name":"time left","label":"off for:","format":"{{msg.payload}}","layout":"row-spread","x":900,"y":240,"wires":[]},{"id":"138a8f18.672b59","type":"change","z":"7456984c.3829b8","name":"format remaining","rules":[{"t":"set","p":"payload","pt":"msg","to":"$exists(msg.payload[0]) ? msg.payload[0].toGo & \" of \" & msg.payload[0].duration & \" left\" : \"not running\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":240,"wires":[["9b0e82cc.7e52c"]]},{"id":"36f8b574.b25cfa","type":"debug","z":"7456984c.3829b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":160,"wires":[]},{"id":"d7c34dd1.1e1a78","type":"ui_group","name":"Group 1","tab":"be046ef4.021378","order":1,"disp":true,"width":6},{"id":"be046ef4.021378","type":"ui_tab","name":"Test","icon":"dashboard","order":6,"disabled":false,"hidden":false}]

You have to enter the time you want to turn of as hh:mm:ss so for 5 hours it would be 05:00.00.

1 Like

Screen Shot 2021-01-06 at 11.59.55

think its working like this now. It just activates when selecting a time.
Adding an activation button ill try and add later.
Thanks for helping to get this to work.

Just released an update. With v1.2.0 you can activate the timer with an incoming payload, like this:


The flow:

[{"id":"f7afe9af.e48698","type":"ui_countdown_timer_switch","z":"d8cb3879.806298","name":"","group":"119bd180.ae754f","order":0,"width":"0","height":"0","countdowns":["60","120","180","240"],"onvalue":"true","onvalueType":"bool","offvalue":"false","offvalueType":"bool","showDropdown":true,"x":620,"y":360,"wires":[[]]},{"id":"63d820f8.2773e","type":"ui_button","z":"d8cb3879.806298","name":"","group":"119bd180.ae754f","order":1,"width":0,"height":0,"passthru":false,"label":"1h","tooltip":"","color":"","bgcolor":"","icon":"","payload":"1","payloadType":"num","topic":"","x":130,"y":340,"wires":[["38c5461.12130ba"]]},{"id":"51876347.1079cc","type":"ui_button","z":"d8cb3879.806298","name":"","group":"119bd180.ae754f","order":1,"width":0,"height":0,"passthru":false,"label":"5h","tooltip":"","color":"","bgcolor":"","icon":"","payload":"300","payloadType":"num","topic":"","x":130,"y":380,"wires":[["38c5461.12130ba"]]},{"id":"38c5461.12130ba","type":"change","z":"d8cb3879.806298","name":"","rules":[{"t":"set","p":"countdown","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":360,"wires":[["f7afe9af.e48698"]]},{"id":"119bd180.ae754f","type":"ui_group","name":"Countdown Timer Switch","tab":"26b38e24.03fa62","order":1,"disp":true,"width":"6","collapse":false},{"id":"26b38e24.03fa62","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false}]
1 Like

Okey, it works but it does have a bug.
Ik I reload the page or open the page on another device the timer resets. and the relay turns off before it finishes its delay. Do I need something extra to prevent this?

Yes it's a known bug but it was fixed in 1.1.2 - the current version is 1.2.0. What version of the countdown-timer-switch are you using?

1 Like

had an old version. Now updated to 1.2.0.
and it helped, thanks :slight_smile:

1 Like

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