Skip to content

Instantly share code, notes, and snippets.

@notenoughtech
Last active June 30, 2020 09:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notenoughtech/f24cb20846f57eec19ed617b25cb61ae to your computer and use it in GitHub Desktop.
Save notenoughtech/f24cb20846f57eec19ed617b25cb61ae to your computer and use it in GitHub Desktop.
Timer in Dashboard

A near online timer made in Dashboard. You can link it with any automation setup in NodeRED, deploy multiple timers and add schedules to your devices. You can customise the payload to suit your needs. enter image description here

Instructions: You can read all set up instructions here


Features:

  • Modular (as a subflow)
  • Unlimited timers per device
  • Supports “days of the week”
  • works offline
  • Dashboard interface
  • One button reset

You will need the following nodes:

  • node-red-dashboard
  • node-red-contrib-schedex

Settings

  • PayloadON payload message sent on ON action
  • PayloadOFF payload message sent on OFF action
  • TimerNumber timer ID (unique for each timer)

More about me:

If you want to get the latest updates to this project you can follow me via your preferred social media:

And if you feeling like buying me a coffee or supporting me in a more continuous way:

I hope you have enjoyed the project!

[{"id":"20f5b48f.283ffc","type":"subflow","name":"Timer Settings (2)","info":"","category":"","in":[{"x":500,"y":320,"wires":[{"id":"dd09ea56.667bf8"}]}],"out":[{"x":740,"y":320,"wires":[{"id":"dd09ea56.667bf8","port":0}]}],"env":[{"name":"TimerNumber","type":"num","value":""},{"name":"PayloadON","type":"str","value":""},{"name":"PayloadOFF","type":"str","value":""}],"color":"#DDAA99"},{"id":"dd09ea56.667bf8","type":"function","z":"20f5b48f.283ffc","name":"","func":"var x = env.get(\"TimerNumber\"); \nvar y = env.get(\"PayloadON\"); \nvar z = env.get(\"PayloadOFF\"); \n\nflow.set(\"$parent.Timer\"+x, x);\nflow.set(\"$parent.Payload\"+x+\"_on\", y);\nflow.set(\"$parent.Payload\"+x+\"_off\", z);\n\n\n\nmsg.payload = \"Your timer \" + x + \" has been set\";\n\nreturn msg;\n","outputs":1,"noerr":0,"x":600,"y":320,"wires":[[]]},{"id":"860a82e0.55f59","type":"tab","label":"Timer in Dashboard","disabled":false,"info":"A neat online timer made in Dashboard. You can link it with any automation setup in NodeRED, deploy multiple timers and add schedules to your devices. \nYou can customise the payload to suit your needs.\n![enter image description here](https://notenoughtech.com/wp-content/uploads/2019/11/YouTube-Thumb.jpg)\n\n**Instructions:**\nYou can read all [set up instructions here](https://notenoughtech.com/home-automation/nodered-home-automation/a-timer-in-nodered/)\n\n---\n\n**Features**:\n\n - **Modular (as a subflow)**\n - **Unlimited timers per device**\n - **Supports “days of the week”**\n - **works offline**\n - **Dashboard interface**\n - **One button reset** \n\n**You will need the following nodes:**\n\n - node-red-dashboard\n - node-red-contrib-schedex\n---\n# Settings\n - **PayloadON** payload message sent on ON action\n - **PayloadOFF** payload message sent on OFF action\n - **TimerNumber** timer ID (unique for each timer)\n"},{"id":"fa5136ce.2a2c98","type":"schedex","z":"860a82e0.55f59","name":"Timer","passthroughunhandled":false,"suspended":false,"lat":"54.525671","lon":"-1.3113165","ontime":"","ontopic":"","onpayload":"","onoffset":"","onrandomoffset":0,"offtime":"","offtopic":"","offpayload":"","offoffset":"","offrandomoffset":0,"mon":false,"tue":false,"wed":false,"thu":false,"fri":false,"sat":false,"sun":false,"x":1050,"y":520,"wires":[["92cb1dcf.7a3f3"]]},{"id":"90f5d76f.c93ae8","type":"ui_text_input","z":"860a82e0.55f59","name":"T2 On","label":"","tooltip":"","group":"b662ff46.2c09c","order":2,"width":"4","height":"1","passthru":false,"mode":"time","delay":"0","topic":"Ton","x":470,"y":240,"wires":[["45f12d1d.d35c74"]]},{"id":"d77abd7b.e5589","type":"ui_button","z":"860a82e0.55f59","name":"Set ON","group":"b662ff46.2c09c","order":3,"width":"2","height":"1","passthru":false,"label":"{{msg.topic}}","tooltip":"","color":"","bgcolor":"{{msg.color}}","icon":"","payload":"","payloadType":"str","topic":"buttonON","x":840,"y":200,"wires":[["de37d89e.2c1fa8"]]},{"id":"5440d628.4afea8","type":"ui_text_input","z":"860a82e0.55f59","name":"T2 Off","label":"","tooltip":"","group":"b662ff46.2c09c","order":4,"width":"4","height":"1","passthru":false,"mode":"time","delay":"0","topic":"Toff","x":470,"y":280,"wires":[["45f12d1d.d35c74"]]},{"id":"394df375.b6654c","type":"ui_button","z":"860a82e0.55f59","name":"Set OFF","group":"b662ff46.2c09c","order":5,"width":"2","height":"1","passthru":false,"label":"{{msg.topic}}","tooltip":"","color":"","bgcolor":"{{msg.color}}","icon":"","payload":"","payloadType":"str","topic":"buttonOFF","x":840,"y":320,"wires":[["de37d89e.2c1fa8"]]},{"id":"f2301d3e.87eda","type":"ui_button","z":"860a82e0.55f59","name":"Reset Button","group":"b662ff46.2c09c","order":6,"width":"6","height":"1","passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":110,"y":400,"wires":[["c21152eb.de612"]]},{"id":"45f12d1d.d35c74","type":"function","z":"860a82e0.55f59","name":"Set T2 On","func":"var z = flow.get(\"Timer1\");\n\n//if timer on is set\nif(msg.topic === \"Ton\"){\n flow.set(\"timer\"+z+\"_on\", msg.payload);\n msg.color = \"red\";\n msg.topic = \"Press\";\n return [msg, null];\n \n \n}\n\n//if timer off is set\nif(msg.topic === \"Toff\"){\n flow.set(\"timer\"+z+\"_off\", msg.payload);\n msg.color = \"red\";\n msg.topic = \"Press\";\n return [null, msg];\n \n}\n\n// reboot detected\nif(msg.topic === \"reboot\"){\n msg.color = \"grey\";\n msg.topic = \"Select Time\";\n return [msg, msg];\n \n}","outputs":2,"noerr":0,"x":670,"y":260,"wires":[["d77abd7b.e5589"],["394df375.b6654c"]]},{"id":"c21152eb.de612","type":"function","z":"860a82e0.55f59","name":"Reset","func":"var z = flow.get(\"Timer1\");\n\nvar timerlabel = \"Timer \"+ z;\n\n\nflow.set(\"timer\"+z+\"_on\", 0);\nflow.set(\"timer\"+z+\"_off\", 0);\nflow.set(\"T\"+z+\"on\", null);\nflow.set(\"T\"+z+\"off\", null);\n\n\n\nvar msg1 = {payload: \"Timer\"+z+\" reset\", topic: timerlabel,};\nvar msg2 = {payload: 0};\n\nvar msg3 = {color: \"grey\",\n topic: \"Select Time\",\n};\n\nvar msg4 = {payload:{suspended: true}};\nvar msg5 = {payload: true};\n\nreturn [msg1, msg2, msg3, msg4, msg5];","outputs":5,"noerr":0,"x":290,"y":400,"wires":[["5fd8db43.2df974"],["5440d628.4afea8","90f5d76f.c93ae8"],["d77abd7b.e5589","394df375.b6654c"],["fa5136ce.2a2c98"],["8042440f.c4ae28","d9da8537.398b28","d0b50712.8b2098","faf557e7.3aad28","173eae7b.e69872","64c7ff12.b418d","30bb90ad.f1322"]]},{"id":"8042440f.c4ae28","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Mon","tooltip":"","group":"b662ff46.2c09c","order":7,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":560,"wires":[["97132fca.3d7b5"]]},{"id":"d9da8537.398b28","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Tue","tooltip":"","group":"b662ff46.2c09c","order":8,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"tue","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":600,"wires":[["9b547ed1.665b"]]},{"id":"d0b50712.8b2098","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Wed","tooltip":"","group":"b662ff46.2c09c","order":9,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":640,"wires":[["9a004f2c.f4301"]]},{"id":"faf557e7.3aad28","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Thu","tooltip":"","group":"b662ff46.2c09c","order":10,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":680,"wires":[["93cdeb25.9fe6b8"]]},{"id":"173eae7b.e69872","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Fri","tooltip":"","group":"b662ff46.2c09c","order":11,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":720,"wires":[["9f022d2e.00199"]]},{"id":"64c7ff12.b418d","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Sat","tooltip":"","group":"b662ff46.2c09c","order":12,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":760,"wires":[["32c2807c.e7bde"]]},{"id":"97132fca.3d7b5","type":"function","z":"860a82e0.55f59","name":"Mon","func":"var z = flow.get(\"Timer1\");\n\nif(z !== undefined){\n var x = msg.payload;\n flow.set(\"Monday\"+z, x);\n msg.payload = {mon: x};\n\n return msg;\n}\n","outputs":1,"noerr":0,"x":710,"y":560,"wires":[["fa5136ce.2a2c98"]]},{"id":"9b547ed1.665b","type":"function","z":"860a82e0.55f59","name":"Tue","func":"var z = flow.get(\"Timer1\");\n\n\nif(z !== undefined){\n var x = msg.payload;\n flow.set(\"Tuesday\"+z, x);\n msg.payload = {tue: x};\n \n return msg;\n\n}\n\n","outputs":1,"noerr":0,"x":710,"y":600,"wires":[["fa5136ce.2a2c98"]]},{"id":"9a004f2c.f4301","type":"function","z":"860a82e0.55f59","name":"Wed","func":"var z = flow.get(\"Timer1\");\nif(z !== undefined){\n\n\nvar x = msg.payload;\nflow.set(\"Wednesday\"+z, x);\nmsg.payload = {wed: x};\n\nreturn msg;\n\n}","outputs":1,"noerr":0,"x":710,"y":640,"wires":[["fa5136ce.2a2c98"]]},{"id":"93cdeb25.9fe6b8","type":"function","z":"860a82e0.55f59","name":"Thu","func":"var z = flow.get(\"Timer1\");\nif(z !== undefined){\nvar x = msg.payload;\nflow.set(\"Thursday\"+z, x);\nmsg.payload = {thu: x};\n\nreturn msg;\n}","outputs":1,"noerr":0,"x":710,"y":680,"wires":[["fa5136ce.2a2c98"]]},{"id":"9f022d2e.00199","type":"function","z":"860a82e0.55f59","name":"Fri","func":"var z = flow.get(\"Timer1\");\n\nif(z !== undefined){\nvar x = msg.payload;\nflow.set(\"Friday\"+z, x);\nmsg.payload = {fri: x};\n}\nreturn msg;","outputs":1,"noerr":0,"x":710,"y":720,"wires":[["fa5136ce.2a2c98"]]},{"id":"32c2807c.e7bde","type":"function","z":"860a82e0.55f59","name":"Sat","func":"var z = flow.get(\"Timer1\");\nif(z !== undefined){\n\nvar x = msg.payload;\nflow.set(\"Saturday\"+z, x);\nmsg.payload = {sat: x};\n}\nreturn msg;","outputs":1,"noerr":0,"x":710,"y":760,"wires":[["fa5136ce.2a2c98"]]},{"id":"34f20933.30f306","type":"function","z":"860a82e0.55f59","name":"Sun","func":"var z = flow.get(\"Timer1\");\n\nif(z !== undefined){\nvar x = msg.payload;\nflow.set(\"Sunday\"+z, x);\nmsg.payload = {sun: x};\n}\nreturn msg;","outputs":1,"noerr":0,"x":710,"y":800,"wires":[["fa5136ce.2a2c98"]]},{"id":"85912f07.bdb98","type":"inject","z":"860a82e0.55f59","name":"","topic":"","payload":"Started!","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":620,"y":440,"wires":[["45eb25e9.cacb6c"]]},{"id":"45eb25e9.cacb6c","type":"function","z":"860a82e0.55f59","name":"Apply stored values ","func":"var z = flow.get(\"Timer1\");\nvar pon = flow.get(\"Payload\"+z+\"_on\");\nvar poff = flow.get(\"Payload\"+z+\"_off\");\n\n\n// restore days\nvar mon = flow.get(\"Monday\"+z);\nvar tue = flow.get(\"Tuesday\"+z);\nvar wed = flow.get(\"Wednesday\"+z);\nvar thu = flow.get(\"Thursday\"+z);\nvar fri = flow.get(\"Friday\"+z);\nvar sat = flow.get(\"Saturday\"+z);\nvar sun = flow.get(\"Sunday\"+z);\n\n//restore timers\nvar timeON = flow.get(\"T\"+z+\"on\");\nvar timeOFF = flow.get(\"T\"+z+\"off\");\nvar timerlabel = \"Timer \"+ z;\n\n\nmsg.payload = { \"ontime\": \"ontime \" + timeON, \"onpayload\": pon,\n \"offtime\": \"offtime \" + timeOFF, \"offpayload\": poff,\n \"mon\": mon,\n \"tue\": tue,\n \"wed\": wed,\n \"thu\": thu,\n \"fri\": fri,\n \"sat\": sat,\n \"sun\": sun\n}\n\n \nreturn msg;","outputs":1,"noerr":0,"x":830,"y":440,"wires":[["fa5136ce.2a2c98"]]},{"id":"b43b80e8.cf98d","type":"comment","z":"860a82e0.55f59","name":"Reboot Fix","info":"","x":600,"y":400,"wires":[]},{"id":"30742d84.196c52","type":"inject","z":"860a82e0.55f59","name":"","topic":"reboot","payload":"Started!","payloadType":"str","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":80,"y":600,"wires":[["442289b7.850598"]]},{"id":"ceea6095.7c354","type":"comment","z":"860a82e0.55f59","name":"Set Button","info":"","x":440,"y":120,"wires":[]},{"id":"5fd8db43.2df974","type":"ui_toast","z":"860a82e0.55f59","position":"top right","displayTime":"3","highlight":"","sendall":true,"outputs":0,"ok":"OK","cancel":"","raw":false,"topic":"","name":"","x":250,"y":280,"wires":[]},{"id":"de37d89e.2c1fa8","type":"function","z":"860a82e0.55f59","name":"On button press","func":"var z = flow.get(\"Timer1\");\nvar pon = flow.get(\"Payload\"+z+\"_on\");\nvar poff = flow.get(\"Payload\"+z+\"_off\");\n\nfunction calcTime(hh){\n var hours = (\"0\"+Math.floor((hh%86400)/3600)).slice(-2);\n var minutes = (\"0\"+Math.floor((hh%3600)/60)).slice(-2);\n return hours + \":\" + minutes;\n}\n\nvar time = null;\nvar timerlabel = \"Timer \"+ z;\n\n\n// when button on is pressed\nif(msg.topic === \"buttonON\"){\n var t1 = flow.get(\"timer\"+z+\"_on\")/1000;\n time = calcTime(t1);\n flow.set(\"T\"+z+\"on\", time);\n //send to text element\n //var msg1 = {payload: \"SET\", topic: timerlabel, color: \"green\"};\n //send to schedex\n var msg1 = {payload: {ontime: \"ontime \" + time,\n onpayload: pon,\n suspended: false},\n topic: \"SET\",\n color: \"green\"};\n return [msg1,null];\n}\n// when button off is pressed\nif(msg.topic === \"buttonOFF\"){\n var t2 = flow.get(\"timer\"+z+\"_off\")/1000;\n time = calcTime(t2);\n flow.set(\"T\"+z+\"off\", time);\n //send to text element\n //var msg3 = {payload: \"SET\", topic: timerlabel, color: \"green\"};\n //send to schedex\n var msg2 = {payload: {offtime: \"offtime \" + time,\n offpayload: poff,\n suspended: false},\n topic: \"SET\",\n color: \"green\"};\n return [null,msg2];\n \n}\n\nreturn msg;","outputs":2,"noerr":0,"x":1060,"y":240,"wires":[["fa5136ce.2a2c98","d77abd7b.e5589"],["fa5136ce.2a2c98","394df375.b6654c"]]},{"id":"64927dcb.95eb24","type":"inject","z":"860a82e0.55f59","name":"Set after deployment","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":60,"wires":[["622b7006.10068"]]},{"id":"400257e.47ba9a8","type":"debug","z":"860a82e0.55f59","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":490,"y":60,"wires":[]},{"id":"622b7006.10068","type":"subflow:20f5b48f.283ffc","z":"860a82e0.55f59","name":"Settings","env":[{"name":"TimerNumber","value":"1","type":"num"},{"name":"PayloadON","value":"true","type":"bool"},{"name":"PayloadOFF","value":"false","type":"bool"}],"x":340,"y":60,"wires":[["400257e.47ba9a8"]],"icon":"node-red/cog.svg"},{"id":"30bb90ad.f1322","type":"ui_switch","z":"860a82e0.55f59","name":"","label":"Sun","tooltip":"","group":"b662ff46.2c09c","order":13,"width":"2","height":"1","passthru":true,"decouple":"false","topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":550,"y":800,"wires":[["34f20933.30f306"]]},{"id":"92cb1dcf.7a3f3","type":"debug","z":"860a82e0.55f59","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1270,"y":520,"wires":[]},{"id":"442289b7.850598","type":"function","z":"860a82e0.55f59","name":"Restore Dashboard","func":"var z = flow.get(\"Timer1\");\n\nif(z !== undefined){\n var mon = flow.get(\"Monday\"+z);\n var tue = flow.get(\"Tuesday\"+z);\n var wed = flow.get(\"Wednesday\"+z);\n var thu = flow.get(\"Thursday\"+z);\n var fri = flow.get(\"Friday\"+z);\n var sat = flow.get(\"Saturday\"+z);\n var sun = flow.get(\"Sunday\"+z); \n\n // update dashboard days\n var msg5 = {payload: mon};\n var msg6 = {payload: tue};\n var msg7 = {payload: wed};\n var msg8 = {payload: thu};\n var msg9 = {payload: fri};\n var msg10 = {payload: sat};\n var msg11 = {payload: sun};\n \n \n // update dashboard times\n var timeON = flow.get(\"T\"+z+\"on\");\n var timeOFF = flow.get(\"T\"+z+\"off\");\n var msg1 = {payload: timeON};\n var msg2 = {payload: timeOFF};\n \n \n // update dash buttons\n var msg3 = {payload: {color: \"green\"}, topic: \"SET\"};\n var msg4 = {payload: {color: \"green\"}, topic: \"SET\"};\n \n \nreturn[msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, msg9, msg10, msg11]; \n \n}\n\n","outputs":11,"noerr":0,"x":250,"y":660,"wires":[["90f5d76f.c93ae8"],["5440d628.4afea8"],["d77abd7b.e5589"],["394df375.b6654c"],["8042440f.c4ae28"],["d9da8537.398b28"],["d0b50712.8b2098"],["faf557e7.3aad28"],["173eae7b.e69872"],["64c7ff12.b418d"],["30bb90ad.f1322"]]},{"id":"b662ff46.2c09c","type":"ui_group","z":"","name":"Evening","tab":"404aace1.054814","order":2,"disp":true,"width":"6","collapse":false},{"id":"404aace1.054814","type":"ui_tab","z":"","name":"Entry Light","icon":"dashboard","order":9,"disabled":false,"hidden":false}]
@jesperuk
Copy link

Would be great, but not working for me, no payload or persistence. I've enabled file memory, but nothing.
It's like flow.set is not be called, i can the the values written in context data

@notenoughtech
Copy link
Author

notenoughtech commented May 27, 2020

Would be great, but not working for me, no payload or persistence. I've enabled file memory, but nothing.
It's like flow.set is not be called, i can the the values written in context data

I found the typo in the subflow - uploaded the latest one just now - please try now

@martynwheeler
Copy link

Hi,
This looks great, but I am struggling a little (just started using node red). I have imported your flow and dragged the created subflow into my existing flow. However, I don't see any environment variables? Could you also please give a little more explanation as to how to display the timer onto my dashboard? Sorry, for the questions. Thanks, Martyn

@notenoughtech
Copy link
Author

notenoughtech commented Jun 27, 2020 via email

@martynwheeler
Copy link

Thanks for the pointers. I will read and see if I can work it out. I have a habit of diving into something and try to figure it out as I go along but sometimes there is no substitute for a bit of background research.

@martynwheeler
Copy link

Hello Mat,
I have read through your tutorials and they are a very good intro to node-red, thanks for spending the time writing them. However, I am still slightly confused as to how to use you timer. Here is what I have done so far:

I have imported your flow onto my node red instance. This has now put a timer on my dashboard. I can enter times and press the buttons. As I understand it I would need to duplicate the flow for every timer to get another set of dashboard widgets?

To use the timer I drag the subflow onto a flow that I am working on as per the instruction:
"I promised the node to be simple to deploy and use. To use it in your NodeRED flow, deploy the subflow and edit the environmental variables."

This is where I am getting stuck! I am probably being thick and as I am a bit new to node-red but I cannot figure out how to wire up the settings? I am not sure where to edit the environmental variables?

If I press the button on the timer in your flow it says the timer has been set. If I wire up a debug to the timer on the far right of your flow nothing happens when the timer start that I have set in the dashboard is reached.

Sorry for the length of my question, just a couple of hints to get me going would be really helpful? Thanks again for your hard work on this.

Martyn

@notenoughtech
Copy link
Author

notenoughtech commented Jun 28, 2020 via email

@martynwheeler
Copy link

martynwheeler commented Jun 28, 2020 via email

@notenoughtech
Copy link
Author

notenoughtech commented Jun 28, 2020 via email

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 28, 2020

Ok, I have to ask:
The node On button press (Function node). It has 4 outputs. Yet only 2 are used.
Why?

The times. It would be nice if there was a way to customise it to show 24 hour time not this am/pm stuff?

You have two inject nodes (something to do with rebooting) They aren't set to inject once so when do they inject their payloads?

For reasons unknown, when I imported the code, I also got a "free" sub-flow. It doesn't seem to be used by your flow, so what does it do?

The _Apply stored values _ node too had 4 outputs and yet only 3 are used.

And the Reset node has an unused output too.

Reading your page in its list of features you say: Modular (as a subflow)
How is this done?
I have since found the 3 nodes at the top of the flow.
They don't really do anything as there too the inject node is not set to inject on startup.

Throughout the code/flow you have var z = flow.get("Timer1"); yet the notification node has a topic of Timer1

Context variable names are not handled nicely. There is a bunch of undefined names in the list.

Screenshot from 2020-06-29 07-57-09

And lastly - for now - You haven't really shown anyone where the output is.

(And more problems:)

The Reset node:
(code)

flow.set("$parent.timer"+z+"_on", 0);
flow.set("$parent.timer"+z+"_off", 0);
flow.set("$parent.T"+z+"on", null);
flow.set("$parent.T"+z+"off", null);

I don't think that is right. $parent is for sub-flows, and as this is not a sub-flow you are making your life difficult with variable names.

Another problem:

The node: on button press:

var z = flow.get("Timer1");
var pon = flow.get("PayloadON");
var poff = flow.get("PayloadOFF");

WRONG.

The names you used are/were: Payload(n)_on and Payload(n)_off

In the part where you try to restore values (apply stored values) function node:

var msg1 = {payload: {ontime: "ontime " + timeON}, onpayload: pon,};
var msg2 = {payload: {offtime: "offtime " + timeOFF}, offpayload: poff,};
var msg3 = {topic: timerlabel};
var msg4 = {payload : { "mon": mon,
                "tue": tue,
                "wed": wed,
                "thu": thu,
                "fri": fri,
                "sat": sat,
                "sun": sun,}
};
                     
    
return [msg1, msg2, msg3];

So why did you go to all the trouble of making a msg4 if you aren't going to use it?

@notenoughtech
Copy link
Author

notenoughtech commented Jun 28, 2020 via email

@Just-another-pleb
Copy link

Errrr.......

I'm walking away from it.

It is a good idea, but there are too many problems with it.

The subflow commands to get variable names/values when it is not a subflow... ALARM BELLS.

The code:
var pon = flow.get("PayloadON");

When there is no flow.get("PayloadON") but rather Payload(x)_on is also a huge worry.
Where x is the timer's number.

The code can not work as posted.

@notenoughtech
Copy link
Author

notenoughtech commented Jun 29, 2020 via email

@notenoughtech
Copy link
Author

OK I think I fixed the instances now.

I appreciate the time you took to look into certain aspects. It helped me to realise where things went sideways. I'm doing some testing now - and I will upload the final version when done.

@notenoughtech
Copy link
Author

Ok

I believe everything should be addressed now.
As a bonus, I added a flow to restore the dashboard on reboot.
Flow no longer should report undefined days.

  • import
  • use setting subflow set timer ID and payload ON|OFF (default 1, true, false)
  • go to dashboard and press reset

Enjoy - any issues let me know

@Just-another-pleb
Copy link

I really think you need to reply to.... martynwheeler and tell him.

He's the one interested.

@notenoughtech
Copy link
Author

I really think you need to reply to.... martynwheeler and tell him.

He's the one interested.

I emailed him - just wanted to leave the note for anyone else following this. Thanks for your help!

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 29, 2020

(I'm a gluten for punishment)

REST node.....
Code:

var z = flow.get("Timer1");
var timerlabel = "Timer "+ z;

Yeah, right.
flow.get("Timer1");

I can nearly let that slide, but why the 1?

There isn't a flow variable: Timer1. But there is a Timer0.

And now have undefined names for the on/off payloads which you have (now) removed from the code.

This if some of you code from the applied stored values node:

var z = flow.get("Timer1");
var pon = flow.get("Payload"+z+"_on");
var poff = flow.get("Payload"+z+"_off");

If there are no flow variables set, it won't work.

You need to make the line/s: `var pon = flow.get("Payload"+z+"_on) || 1;` `var poff = flow.get("Payload"+z+"_off) || 0;` as an example to set the default on/off payloads to 1 and 0.

This is a mess up mentioned below.

In your sub-flow you index things as:

var x = env.get("TimerNumber"); 
var y = env.get("PayloadON"); 
var z = env.get("PayloadOFF"); 

But in the flow you are:

var z = flow.get("Timer1");

Won't work.

I'm not a big user of sub-flows, but at the flow level when I open (double click) on the sub-flow's node I get a screen setting the payload_on and _off (etc) values.
There is nothing in those fields, so they won't ..... work.
(I now get that they need to be set at start up to work.)

ARGH!

There is also (I think) a disconnect between the timer number and the name. You are calling it timerz (what ever) in some parts and TimerNumber in others, and Timer1 in others.

I don't think that is helping either.

If you expand this and have multiple timers, it is going to get nasty very quickly.

The problem - as I see it - is that you have a flow.get/set( of a number that includes the name "timer" in it.
So timer0 is set to 0. timer1 is set to 1, timer2 is set to 2 and so on.
Yes, this is needed for multiple instances and to keep them separated.
It sort of makes it nice because you can flow.get("Timer"+z) to get the value.
But really you don't need to do that as the z already has the value.
All you need to get are the on/off payloads and times.
That extra line isn't needed.

But you missed that detail when you are retrieving the variables because you are calling it Timer1 in some cases when it should be Timerz (I think is your way)

@notenoughtech
Copy link
Author

Timer1
Is an unfortunate leftover from one of the attempts to make this modular with a subflow. Which at this point I could name anything but serves as TIMER_ID really and can be pretty much anything). My idea of nesting these inside subflows didn't work well and the result was a lot of broken code that you pointed out - something I partially fixed on my end but for some reason never updated online template.

Like a lot of my fun projects, these start as one thing... and then head in the direction not always controlled by me - but by what I can figure out :)

Therefore:

`var z = flow.get("Timer1");
var pon = flow.get("Payload"+z+"_on");
var poff = flow.get("Payload"+z+"_off");`

Won't break as Timer1 is always set. You need this if you want to have multiple instances. At least this is how I made it work. This is set by the subflow and env vars.

These call only from the subflow level so

var x = env.get("TimerNumber"); 
var y = env.get("PayloadON"); 
var z = env.get("PayloadOFF"); 

are never used in the flow. You enter them in the subflow to create the basic configuration.

AS per this:

The problem - as I see it - is that you have a flow.get/set( of a number that includes the name "timer" in it.
So timer0 is set to 0. timer1 is set to 1, timer2 is set to 2 and so on.
Yes, this is needed for multiple instances and to keep them separated.
It sort of makes it nice because you can flow.get("Timer"+z) to get the value.
But really you don't need to do that as the z already has the value.

Each timer has "Timer1" property so it should not cause any problems - its purely my bad naming convention at this point

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 29, 2020

I don't know what happened to a post. I said I was a gluten for punishment.
Seems I am, I lost the post it seems.

There is still a problem. The sub-flow making a new copy of itself isn't helping.

I would suggest you inject the set after deployment node to have the inject after boot set also.
That gets things established better.

As I see it you have two parts: (As before - and still)
1 - the three nodes at the top that "set things up".
2 - the bigger flow which you say can be duplicated to multiple instances.

The thing is: it can't.
You don't have any where in the second part that sets the number for that part of the flow.

Pressing the inject node for the first 3 nodes, sets things up. You still haven't made it an auto inject at boot node.
Then the rest works for instance 1 of the nodes.
But if you make a second instance, it will only read #1's settings.

This is because in the bigger part in the function nodes you call it Timer1 - which is the default node (though not set up).
As soon as you get Timer2 and so on, all the function nodes read Timer1's settings.

You get stuck because though it is trapped on the flow, you can cheat and have the different instances on different sheets. But that goes against the idea of it being repeatable, and the advantage of storing things in the sub-flow's contexts.

See what you can do to fix that problem.

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 29, 2020

Won't break as Timer1 is always set

No it isn't.

It is only set if the initial 3 nodes are triggered.
Which is why I am saying it needs to have the inject once ticked.

(Trying to not flood this, but......)
I'll edit the post.

A way you could get around it is have something like this bit of code in all the function nodes:

if (msg.topic == "SETUP")
{
    context.se("TIMER",msg.payload);
    return;
}

var z = flow.get("Timer"+context.get("TIMER"));

At boot (which you seem to avoid like the plague) a message is sent to all the function nodes for that instance of the timer and it sets their timer number.

Then if you have a second instance, you would inject a message with a 2 as the payload. (Topic set as well.)

@notenoughtech
Copy link
Author

OK I see when I cornered myself now - darn! right it's 7am right now and I have been up all night I will fight with this with a fresh brain! Thanks for the brain lessons :)

@notenoughtech
Copy link
Author

Won't break as Timer1 is always set

No it isn't.

It is only set if the initial 3 nodes are triggered.
Which is why I am saying it needs to have the inject once ticked.

That's intended actually.
as you have to configure the payloadON|OFF to match your preference

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 29, 2020

OK I see when I cornered myself now - darn! right it's 7am right now and I have been up all night I will fight with this with a fresh brain! Thanks for the brain lessons :)

Ok, no problems.

Get a bit of rest and come back with a clear mind.

Thought!
Rather than sending the values to the sub-flow via the variables, why not send it in a msg?
Then you have everything you need from a dynamic message.

The on/off messages can be sent from the inject node.
(Well, probably more a change node loaded up with all those things, but.....)

With me?

So the node (in the sub-flow) would look like this: (example structure)

//  Old code
//var x = env.get("TimerNumber"); 
//var y = env.get("PayloadON"); 
//var z = env.get("PayloadOFF"); 

//  New code
var x = msg.payload.TimerNumber;
var y = msg.paylaod.PayloadON;
var z = msg.payload.PayloadOFF;

flow.set("$parent.Timer"+x, x);
flow.set("$parent.Payload"+x+"_on", y);
flow.set("$parent.Payload"+x+"_off", z);

msg.payload = "Your timer " + x + " has been set";

return msg;

However, I guess the way I suggested is not for persistent variables, which you are setting. So sorry.

@Just-another-pleb
Copy link

I was thinking - and I have been told that is dangerous - however.

I am just curious to the advantage of doing things this way.

The nodes all need to be connected together anyway - now (in retrospect) - to assign their timer number.

So you are feeding their data into a sub-flow that sets things in/on the parent flow's context.

All that could be done at the parent level anyway with the function node you have as a sub-flow in the flow and setting the flow context data there and then, rather than sending the message into a subflow that then sets the parent's context values.

Seems a bit redundant how you are doing it.

Just saying.

@notenoughtech
Copy link
Author

I will see what I can do with that info.
I have a couple of things to go through before I can get back to this flow. Now sure if you tried the beta3 for nodeRED - there are promising modifications to the function node that work on deployment. Something tells me I will be using that moving forward instead. Just need to get more familiar with it

@Just-another-pleb
Copy link

Just-another-pleb commented Jun 30, 2020

Latest code better.
Not perfect though. Sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment