Set time function on dashboard

1st question:
what do you mean by opposite? This function turns a time in the format of milliseconds since midnight into an hours and minutes array.
So you want to turn an hours and minutes array into milliseconds since midnight?
For example a function node with this:

msg.payload = (msg.payload[0]*3600000)+(msg.payload[1]*60000);
return msg;

will reverse the hours minutes array back to milliseconds since midnight.

2nd question:
I use this format of pushing the hours and minutes into an array because it makes it very easy to check against it in the function that checks if its time as you can just access the hours and minutes by their index instead of having to do further transformation.
So you could also change that function to accept your format.
If you want to change a time from your “hh,mm” format string to an array you need to split the string and parse it to a number.
A function node with this:

let array = msg.payload.split(“,”);
msg.payload = array.map(element => parseFloat(element));
return msg;

will do that.
I really recommend that you look at array methods in javascript:


and at string methods:

as those are all the tools you need to solve those problems.
Best regards Johannes

Hey Johannes it really helps a lot now one last thing I was trying to put multiple on hours but it did't work can you help me?

So like in the image I want to put like 4 different times to send on i need a for? Because it only sends the last hour injected

You could use a schedule node.

That allows something like what you want.

1 Like

Yes, you would have to set each time to its own flow unique variable and than also check for this flow variable in the check on & off function.
The flow i gave you was more meant as an inspiration giving you some basic methods to build a solution yourself.
You can use it to set multiple schedules but you will have to run a complete copy of the flow for each on & off pair where you change the variable names to something unique in the change nodes and check for those unique variables in the check on & off function.
But this approach will get tedious very quickly. For many schedules You would probably want to use some kind of scheduling node like @Trying_to_learn recommended. There is for example https://flows.nodered.org/node/node-red-contrib-timerswitch which is very simple but you cant set it with an input so its not combine able with the ui but works very reliably once set or something more complex but also vastly more powerful like cron-plus https://flows.nodered.org/node/node-red-contrib-cron-plus where @Steve-Mcl already shared a flow with you above that is an example of an ui driven scheduler with this node.
If you want to continue with you own solution and make it scalable you will probably have to change the format and do something like consolidating all time values you want to schedule into one array of arrays that also includes the commands to be sent. To do this you will have to do a lot of coding which is sure good to learn but you have to decide if your up for it.

Johannes

@lalo just so you know, the above example I posted will also accept a simple time (or comma separated list of times)

I realise the UI layout may not be how you need it but that can easily be changed.

I'm unsure why this discussion is still going on tbh but is there any specific reason you are still pursuing this?

1 Like

I guess it is still going only because @lalo still doesn't get it and is trying to learn.

We all learn in different ways.

I admit I was bored and I agree with @Trying_to_learn that if @lalo wants to continue down this path who are we to stop him. @Steve-Mcl I often build new solutions just for the fun of it even if the problem has already been solved by others.
So here we go (https://flows.nodered.org/node/node-red-node-ui-table needs to be installed):

[{"id":"8233c164.1bcb58","type":"function","z":"f954564f.03e718","name":"check command","func":"const time = new Date(msg.payload);\nconst hour = time.getHours();\nconst minute = time.getMinutes();\nlet timef = \"\";\nif(minute<10){\n    if(hour>9){\n        timef = String(hour) + \":0\" + String(minute);\n    } else {\n        timef = \"0\"+ String(hour) + \":0\" + String(minute);\n    }\n} else {\n    if(hour>9){\n        timef = String(hour) + \":\" + String(minute);\n    } else {\n        timef = \"0\"+ String(hour) + \":\" + String(minute);\n    }\n}\nconst schedule = flow.get(\"schedule\");\nschedule.forEach(element => {\n    if(element.time == timef) node.send({payload:element.command,item:element.item});\n});\nnode.done();\nreturn;","outputs":1,"noerr":0,"x":470,"y":3660,"wires":[["2af56f10.1de678","fdd91332.9b43"]]},{"id":"ef2b9f11.6427a","type":"inject","z":"f954564f.03e718","name":"","topic":"","payload":"","payloadType":"date","repeat":"60","crontab":"","once":false,"onceDelay":0.1,"x":260,"y":3660,"wires":[["8233c164.1bcb58"]]},{"id":"37d3efaa.0c2018","type":"ui_text","z":"f954564f.03e718","group":"5b810075.4590d","order":1,"width":0,"height":0,"name":"","label":"Pump","format":"{{msg.payload}}","layout":"row-spread","x":850,"y":3640,"wires":[]},{"id":"69efc588.ee6c0c","type":"ui_form","z":"f954564f.03e718","name":"","label":"","group":"7d6a9a85.b4af24","order":1,"width":0,"height":0,"options":[{"label":"Item","value":"item","type":"text","required":true,"rows":null},{"label":"Command","value":"command","type":"text","required":true,"rows":null},{"label":"Time","value":"time","type":"text","required":true,"rows":null}],"formValue":{"item":"","command":"","time":""},"payload":"","submit":"Add","cancel":"","topic":"","x":430,"y":3520,"wires":[["d81b69c7.453cd8","6f55d1dd.163138"]]},{"id":"d81b69c7.453cd8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.time","pt":"msg","to":"enter a time in the hh:mm format","tot":"str"},{"t":"set","p":"payload.command","pt":"msg","to":"enter a command","tot":"str"},{"t":"set","p":"payload.item","pt":"msg","to":"enter the item to switch","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":3520,"wires":[["69efc588.ee6c0c"]]},{"id":"27cb3d9b.1c51b2","type":"ui_table","z":"f954564f.03e718","group":"7d6a9a85.b4af24","name":"","order":2,"width":"6","height":"6","columns":[],"outputs":1,"cts":true,"x":770,"y":3520,"wires":[["41b7de7e.3e51f8"]]},{"id":"6f55d1dd.163138","type":"function","z":"f954564f.03e718","name":"add to schedule","func":"let schedule = flow.get(\"schedule\") || [];\nlet command = msg.payload;\nlet newindex = null;\nif(schedule.length > 0){\n    for(i=0;i<schedule.length-1;i++){\n        if(command.time >= schedule[i].time && command.time < schedule[i+1].time){\n            newindex = i+1;\n        }\n    }\n    if(newindex !== null){\n        schedule.splice(newindex,0,command);\n    } else if (command.time < schedule[0].time){\n        schedule.splice(0,0,command);\n    } else {\n        schedule.push(command);\n    }\n} else {\n    schedule.push(command);\n}\nflow.set(\"schedule\", schedule);\nmsg.payload = schedule;\nreturn msg;","outputs":1,"noerr":0,"x":600,"y":3520,"wires":[["27cb3d9b.1c51b2","c4a381a3.dace88"]]},{"id":"e962955b.b0359","type":"function","z":"f954564f.03e718","name":"remove from schedule","func":"if (msg.payload == \"Cancel\") return null;\nlet schedule = flow.get(\"schedule\");\nschedule.splice(msg.row, 1);\nflow.set(\"schedule\", schedule);\nmsg.payload = schedule;\nreturn msg;","outputs":1,"noerr":0,"x":1360,"y":3520,"wires":[["27cb3d9b.1c51b2","c4a381a3.dace88"]]},{"id":"eec5a4b.3af5bd8","type":"ui_toast","z":"f954564f.03e718","position":"dialog","displayTime":"3","highlight":"","sendall":true,"outputs":1,"ok":"OK","cancel":"Cancel","raw":false,"topic":"","name":"","x":1150,"y":3520,"wires":[["e962955b.b0359"]]},{"id":"41b7de7e.3e51f8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"content","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"Delete this schedule?","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Delete","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":960,"y":3520,"wires":[["eec5a4b.3af5bd8"]]},{"id":"c4a381a3.dace88","type":"delay","z":"f954564f.03e718","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":780,"y":3460,"wires":[["ef3518ad.80b7e8"]]},{"id":"59f1e488.99455c","type":"ui_ui_control","z":"f954564f.03e718","name":"","events":"all","x":1140,"y":3460,"wires":[[]]},{"id":"ef3518ad.80b7e8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"tab\":\"\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":960,"y":3460,"wires":[["59f1e488.99455c"]]},{"id":"2af56f10.1de678","type":"switch","z":"f954564f.03e718","name":"which item","property":"item","propertyType":"msg","rules":[{"t":"eq","v":"Pump","vt":"str"},{"t":"eq","v":"Irrigation","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":670,"y":3660,"wires":[["37d3efaa.0c2018"],["e2210cd1.e8f83"]]},{"id":"e2210cd1.e8f83","type":"ui_text","z":"f954564f.03e718","group":"5b810075.4590d","order":2,"width":0,"height":0,"name":"","label":"Irrigation","format":"{{msg.payload}}","layout":"row-spread","x":860,"y":3680,"wires":[]},{"id":"fdd91332.9b43","type":"debug","z":"f954564f.03e718","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":650,"y":3780,"wires":[]},{"id":"c54ebfc9.b3594","type":"inject","z":"f954564f.03e718","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":250,"y":3460,"wires":[["d81b69c7.453cd8"]]},{"id":"5b810075.4590d","type":"ui_group","z":"","name":"Items","tab":"b83efc1a.370b88","order":1,"disp":true,"width":"6","collapse":false},{"id":"7d6a9a85.b4af24","type":"ui_group","z":"","name":"Scheduler","tab":"b83efc1a.370b88","order":2,"disp":true,"width":"6","collapse":false},{"id":"b83efc1a.370b88","type":"ui_tab","z":"","name":"Scheduler","icon":"call_split","order":2,"disabled":false,"hidden":false}]



This is an evolution of the last post and kind of an intermediate step to something like using the cron-plus.
Instead of saving each time to a variable we save one object for each time to an array variable.
This object includes An item to be switched, a command to be executed and a time to execute this command.
Because this is quite simplistic we have to input those vars in the correct format. we use the ui form node for this. The item name has to be the exact item name! Same for command. The time needs to be in the hh:mm format for this to work. eg "11:30".
We than in the following function node add the object that the form node outputs once you click add (something like this:

{"item":"Pump","command":"ON","time":"12:14"}

) to the flow.schedule var. We do do something else, we sort the added object according to the time into this var. So the times will always be in chronological order.
We also return the schedule array to the table node to display our schedule.
If you click one of the entered schedules you can also delete it from the flow.schedule array.
I changed the function node that previously checked for the on&off vars every minute to instead check for the time property of each object in the flow.schedule array.
So it now checks for the hh:mm format.
It also adds item property to the message so we can use a simple switch node to sort the commands to the correct items.
You can add and delete as many time schedules as you want and they will always be sorted chronologically.
Have fun playing around and I hope you can learn something from this.
Be aware that this is a very rough prototype I threw together in half an hour and havent tested it extensively for errors.
Johannes

PS I hope this makes some sense and i added a check if the time is previous to the first time in the collumn or generallly the first

@JGKK @Steve-Mcl @Trying_to_learn Thanks guys i'm taking an online course of Javascript it helps sometimes but i always need like an example I was trying all your recomendations. I really appreciate your help with all the links and documentation that you are giving me i can understand how it works and after that i can get it and also i learn more about Node-RED.

So you dont want to input the schedules from the dashboard like you have been asking us the whole time but instead you want to input them from an external spreadsheet?
You get them in the hh,mm format from the spreadsheet?

i was asking more about some stuffs to try it, but i'm having troubles to understand really what is the structure that i need to make it works. yeah i will get them in the hh,mm format, but i don't know if is the best format, but if there is another way to do it easier for me it's fine on excel is easy to change the format.

why don’t you load the data straight from the spreadsheet?
You can either use the https://flows.nodered.org/node/node-red-contrib-spreadsheet-in to load excel workbooks directly or you save it as an csv and use the inbuild nodes for it. No reason to go via the dashboard.
to convert hh,mm to hh:mm:

const msgformat = msg.payload.replace(/\,/g,":");
msg.payload = msgformat;
return msg;

this uses the replace method https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace and a regex to search for an , and replace it with a :
Johannes

Edit: Can you give as an example on what the data in the spreadsheet looks like? This would make this a whole lot easier?

Edit2: You can even load directly from a google spreadsheet: Google Spreasheet zu Node Red

What you can do is enter the time/s you want with buttons (dashboard inputs) and construct a message to send to the schedule node and it does all the work.

Sorry, I've not been keeping up with this thread all the way through.

@JGKK is doing a good job, but I can see an alternative to using a spread sheet.

Actually looking at what @JGKK has posted, he is a lot smarter than me.

I don't know how to use those fancy things and build tables.

Oh, big admission by me: Earlier I said the schedule node. I really meant the cron+ node.

The cron+ node allows you to send messages to it to do a lot of things.

Small suggestion: Install that node - it comes with examples and they may help you too.

Though, if @JGKK can put tables on the screen and allow you to enter stuff that way. To me: that wins hands down.

I'll respectfully back out of this. It is way above my skill set.

Sorry but I need help with the flow you posted.

It imported ok but I don't get this error:

TypeError: Cannot read property 'forEach' of undefined

This probably happens when you havent put in a schedule yet

Oh. I suspected that. I have abs(0) knowledge of this stuff.

I shall have to see if I can work out how to work out how to stop that happening.

Probably just change

const schedule = flow.get("schedule");

to

const schedule = flow.get("schedule") || [];
1 Like
[{"id":"8233c164.1bcb58","type":"function","z":"f954564f.03e718","name":"check command","func":"const time = new Date(msg.payload);\nconst hour = time.getHours();\nconst minute = time.getMinutes();\nconst lastminute = context.get(\"lastminute\");\nif(lastminute == minute){\n    context.set(\"lastminute\",minute);\n    return null;\n} else {\n    context.set(\"lastminute\",minute);\n}\nlet timef = \"\";\nif(minute<10){\n    if(hour>9){\n        timef = String(hour) + \":0\" + String(minute);\n    } else {\n        timef = \"0\"+ String(hour) + \":0\" + String(minute);\n    }\n} else {\n    if(hour>9){\n        timef = String(hour) + \":\" + String(minute);\n    } else {\n        timef = \"0\"+ String(hour) + \":\" + String(minute);\n    }\n}\nconst schedule = flow.get(\"schedule\") || [];\nschedule.forEach(element => {\n    if(element.time == timef) node.send({payload:element.command,item:element.item});\n});\nnode.done();\nreturn;","outputs":1,"noerr":0,"x":450,"y":3660,"wires":[["2af56f10.1de678"]]},{"id":"ef2b9f11.6427a","type":"inject","z":"f954564f.03e718","name":"","topic":"","payload":"","payloadType":"date","repeat":"20","crontab":"","once":false,"onceDelay":0.1,"x":250,"y":3660,"wires":[["8233c164.1bcb58"]]},{"id":"37d3efaa.0c2018","type":"ui_text","z":"f954564f.03e718","group":"5b810075.4590d","order":1,"width":0,"height":0,"name":"","label":"Pump","format":"{{msg.payload}}","layout":"row-spread","x":810,"y":3640,"wires":[]},{"id":"69efc588.ee6c0c","type":"ui_form","z":"f954564f.03e718","name":"","label":"","group":"7d6a9a85.b4af24","order":1,"width":0,"height":0,"options":[{"label":"Item","value":"item","type":"text","required":true,"rows":null},{"label":"Command","value":"command","type":"text","required":true,"rows":null},{"label":"Time","value":"time","type":"text","required":true,"rows":null}],"formValue":{"item":"","command":"","time":""},"payload":"","submit":"Add","cancel":"","topic":"","x":430,"y":3520,"wires":[["d81b69c7.453cd8","6f55d1dd.163138"]]},{"id":"d81b69c7.453cd8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"payload.time","pt":"msg","to":"enter a time in the hh:mm format","tot":"str"},{"t":"set","p":"payload.command","pt":"msg","to":"enter a command","tot":"str"},{"t":"set","p":"payload.item","pt":"msg","to":"enter the item to switch","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":3520,"wires":[["69efc588.ee6c0c"]]},{"id":"27cb3d9b.1c51b2","type":"ui_table","z":"f954564f.03e718","group":"7d6a9a85.b4af24","name":"","order":2,"width":"6","height":"6","columns":[],"outputs":1,"cts":true,"x":770,"y":3520,"wires":[["41b7de7e.3e51f8"]]},{"id":"6f55d1dd.163138","type":"function","z":"f954564f.03e718","name":"add to schedule","func":"let schedule = flow.get(\"schedule\") || [];\nlet command = msg.payload;\nlet newindex = null;\nif(schedule.length > 0){\n    for(i=0;i<schedule.length-1;i++){\n        if(command.time >= schedule[i].time && command.time < schedule[i+1].time){\n            newindex = i+1;\n        }\n    }\n    if(newindex !== null){\n        schedule.splice(newindex,0,command);\n    } else if (command.time < schedule[0].time){\n        schedule.splice(0,0,command);\n    } else {\n        schedule.push(command);\n    }\n} else {\n    schedule.push(command);\n}\nflow.set(\"schedule\", schedule);\nmsg.payload = schedule;\nreturn msg;","outputs":1,"noerr":0,"x":600,"y":3520,"wires":[["27cb3d9b.1c51b2","c4a381a3.dace88"]]},{"id":"e962955b.b0359","type":"function","z":"f954564f.03e718","name":"remove from schedule","func":"if (msg.payload == \"Cancel\") return null;\nlet schedule = flow.get(\"schedule\");\nschedule.splice(msg.row, 1);\nflow.set(\"schedule\", schedule);\nmsg.payload = schedule;\nreturn msg;","outputs":1,"noerr":0,"x":1360,"y":3520,"wires":[["27cb3d9b.1c51b2","c4a381a3.dace88"]]},{"id":"eec5a4b.3af5bd8","type":"ui_toast","z":"f954564f.03e718","position":"dialog","displayTime":"3","highlight":"","sendall":true,"outputs":1,"ok":"OK","cancel":"Cancel","raw":false,"topic":"","name":"","x":1150,"y":3520,"wires":[["e962955b.b0359"]]},{"id":"41b7de7e.3e51f8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"content","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"Delete this schedule?","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Delete","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":960,"y":3520,"wires":[["eec5a4b.3af5bd8"]]},{"id":"c4a381a3.dace88","type":"delay","z":"f954564f.03e718","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":790,"y":3460,"wires":[["ef3518ad.80b7e8"]]},{"id":"59f1e488.99455c","type":"ui_ui_control","z":"f954564f.03e718","name":"","events":"all","x":1160,"y":3460,"wires":[[]]},{"id":"ef3518ad.80b7e8","type":"change","z":"f954564f.03e718","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"tab\":\"\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":980,"y":3460,"wires":[["59f1e488.99455c"]]},{"id":"2af56f10.1de678","type":"switch","z":"f954564f.03e718","name":"which item","property":"item","propertyType":"msg","rules":[{"t":"eq","v":"Pump","vt":"str"},{"t":"eq","v":"Irrigation","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":650,"y":3660,"wires":[["37d3efaa.0c2018"],["e2210cd1.e8f83"]]},{"id":"e2210cd1.e8f83","type":"ui_text","z":"f954564f.03e718","group":"5b810075.4590d","order":2,"width":0,"height":0,"name":"","label":"Irrigation","format":"{{msg.payload}}","layout":"row-spread","x":820,"y":3680,"wires":[]},{"id":"c54ebfc9.b3594","type":"inject","z":"f954564f.03e718","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":250,"y":3460,"wires":[["d81b69c7.453cd8"]]},{"id":"5b810075.4590d","type":"ui_group","z":"","name":"Items","tab":"b83efc1a.370b88","order":1,"disp":true,"width":"6","collapse":false},{"id":"7d6a9a85.b4af24","type":"ui_group","z":"","name":"Scheduler","tab":"b83efc1a.370b88","order":2,"disp":true,"width":"6","collapse":false},{"id":"b83efc1a.370b88","type":"ui_tab","z":"","name":"Scheduler","icon":"call_split","order":2,"disabled":false,"hidden":false}]

is an updated version with the minor improvement that it injects every 20 seconds but only checks the schedule if the minute has changed. So it should always trigger a schedule within 20 seconds of the time. As I said its a very rough prototype.

1 Like

Thanks.

I shall have to look at it more.

I get what you are doing, but only to make it just that much better, I would try to make a message that is cron+ compatible and send that into cron+.

Then to see the list, inject a command to get the list of enteries from the node and put that into the table.

But that's just a thought.

It would remove the need for the 20 second inject node etc.

look at the link @Steve-Mcl posted above it has already been done way better than i could ever do it:
https://flows.nodered.org/flow/79a66966a6cc655a827872a4af794b94
This is by now no more than a mental exercise to build it on the principle of an injected timestamp.
I think @lalo actually already has all the times in a spreadsheet so Im not quite sure why he did bring the dashboard-ui as an input into play at all as he can just read those values straight from the spreadsheet.

Thanks. I forgot about that. I already have the node installed it is just I got caught up in the story and wanted a nice way to enter jobs like this.

Anyway: I think we are getting off topic.

(Sorry @lalo)