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.