Time Inputs NewBlynk

Thanks, talking about Node-JS (I understand this is the language used in Nodered, not Java Script?) and the availability of different Nodered nodes: what can I do best to improve my skills a bit for being able to setup this? Especially the way the functions are written is someting I don’t understand at all now.

This is a good place to start…

https://nodered.org/docs/user-guide/writing-functions

The thing that most people struggle with is that a function (or any node for that matter) can have only one input, which triggers the node to execute its code.
When you’re writing functions there are often other pieces of information that are needed to do the logic processing (is it dark outside, am I at home at the moment, what are the start/stop times for the timer, what are the active days for the timer etc).

There are several ways around this. You can have multiple message elements in the incoming messages, not just the message payload. This can be useful, but it’s not an approach I normally use.

The other way is to store these values as pieces of data, at either a flow or global level, then retrieve it within the function. This is the approach that is simplest to use, and is discussed in the guide above under “storing data”. It’s not that different to using variables in C++, but its just requires a bit more code to put and get the values from the data store.

Have a read through the tutorial and try dome of the examples, especially the ones where you have multiple outputs (you’ll need that) and storing/retrieving data.

If I get time I’ll pull together some function code examples that will get you started.

Pete.

Here’s a flow that you can import that handles two timers…

[{"id":"d3992c1c4a1f414d","type":"tab","label":"Time Input Widget Testing","disabled":false,"info":"","env":[]},{"id":"ed80c198c81ac38a","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_1 Time Input Widget","pin":"1","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":60,"wires":[["8c825a7cf5a695ee"]]},{"id":"c1ec2fa4500956b4","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"1","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":60,"wires":[]},{"id":"e6507dab04dc22a7","type":"inject","z":"d3992c1c4a1f414d","name":"Sync at startup","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":1020,"y":60,"wires":[["c1ec2fa4500956b4","776f392f26025355"]]},{"id":"3e69839b2d9e3ea2","type":"function","z":"d3992c1c4a1f414d","name":"Process timers","func":"// Calculate the current day number (day_number)\nvar date = new Date();\nvar day_number = date.getDay();\n\n// Calculate the current seconds since midnight (current_seconds_since_midnight)\nvar current_hour = date.getHours();\nvar current_minute = date.getMinutes();\nvar current_seconds = date.getSeconds();\nvar current_seconds_since_midnight = (current_hour * 3600) + (current_minute * 60) + current_seconds;\n\n// Process Timer 1...\n\n// Check if today is an active day for timer 1\nvar active_days_timer_1 = flow.get('active_days_timer_1');       // retreive the array of active days for this timer\nvar is_today_active_timer_1 = active_days_timer_1[day_number];   // check if today is an acive day for this timer (true/false)\n\n// Retreive the stop/start and is stop time tomorrow data for timer_1...\nvar start_time_timer_1 = flow.get('start_time_timer_1');\nvar stop_time_timer_1 = flow.get('stop_time_timer_1');\nvar is_stop_time_tomorrow_timer_1 = flow.get('is_stop_time_tomorrow_timer_1');\n\n\nif (current_seconds_since_midnight == 0)\n{\n    // if it's midnight then set `is_stop_time_tomorrow_timer_1` to false, because it's now tomorrow...\n    is_stop_time_tomorrow_timer_1 = false;\n}\n\nif ((current_seconds_since_midnight > start_time_timer_1 && (current_seconds_since_midnight < stop_time_timer_1) && is_stop_time_tomorrow_timer_1 == false) && (is_today_active_timer_1))\n{\n    // We get here if the current time is between the start and stop times and the stop time isn't tomorrow, and today is an active day \n    var timer_1 = 1; // the timer 1 output wiil be on (1) \n}\n\n\nif ((current_seconds_since_midnight > start_time_timer_1) && (is_stop_time_tomorrow_timer_1) && (is_today_active_timer_1))\n{\n    // We get here if the current time is after the start time and the stop time is tomorrow, and today is an active day \n    var timer_1 = 1; // the timer 1 output wiil be on (1) \n}\n\n\nif ((current_seconds_since_midnight >= stop_time_timer_1) && (is_stop_time_tomorrow_timer_1 == false))\n{\n    // We get here if the current time is after the stop time, and the stop time isn't tomorrow\n    var timer_1 = 0; // the timer 1 output wiil be off (0)\n}\n\n// Process Timer 2...\n\n// Check if today is an active day for timer 2\nvar active_days_timer_2 = flow.get('active_days_timer_2');       // retreive the array of active days for this timer\nvar is_today_active_timer_2 = active_days_timer_2[day_number];   // check if today is an acive day for this timer (true/false)\n\n// Retreive the stop/start and is stop time tomorrow data for timer_2...\nvar start_time_timer_2 = flow.get('start_time_timer_2');\nvar stop_time_timer_2 = flow.get('stop_time_timer_2');\nvar is_stop_time_tomorrow_timer_2 = flow.get('is_stop_time_tomorrow_timer_2');\n\n\nif (current_seconds_since_midnight == 0) {\n    // if it's midnight then set `is_stop_time_tomorrow_timer_1` to false, because it's now tomorrow...\n    is_stop_time_tomorrow_timer_2 = false;\n}\n\nif ((current_seconds_since_midnight > start_time_timer_2 && (current_seconds_since_midnight < stop_time_timer_2) && is_stop_time_tomorrow_timer_2 == false) && (is_today_active_timer_2)) \n{\n    // We get here if the current time is between the start and stop times and the stop time isn't tomorrow, and today is an active day \n    var timer_2 = 1; // the timer 2 output wiil be on (1) \n}\n\n\nif ((current_seconds_since_midnight > start_time_timer_2) && (is_stop_time_tomorrow_timer_2) && (is_today_active_timer_2))\n{\n    // We get here if the current time is after the start time and the stop time is tomorrow, and today is an active day \n    var timer_2 = 1; // the timer 2 output wiil be on (1) \n}\n\n\nif ((current_seconds_since_midnight >= stop_time_timer_2) && (is_stop_time_tomorrow_timer_2 == false)) \n{\n    // We get here if the current time is after the stop time, and the stop time isn't tomorrow\n    var timer_2 = 0; // the timer 2 output wiil be off (0)\n}\n\n\n// Process Timer 3...\n\n\n\n\n// Send the values to the correct outputs...\nreturn [{ 'payload': timer_1 }, { 'payload': timer_2 }]\n\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":720,"wires":[["ee6f988209c5f46e"],["dcc3b2bc65358a82"]],"inputLabels":["trigger"],"outputLabels":["timer_1","timer_2"]},{"id":"0ed39756252f0a60","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":720,"y":700,"wires":[]},{"id":"9ceebf4490b1865a","type":"inject","z":"d3992c1c4a1f414d","name":"Repeat ever second","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":160,"y":720,"wires":[["3e69839b2d9e3ea2"]]},{"id":"8c825a7cf5a695ee","type":"function","z":"d3992c1c4a1f414d","name":"Store On/Off Times etc for Timer_1","func":"// get the partial array of days from Blynk\nvar blynk_day_data = msg.arrayOfValues[3];\nvar active_days = [`dummy`,true,true,true,true,true,true,true];\n\nactive_days[1] = blynk_day_data.includes(\"1\");\nactive_days[2] = blynk_day_data.includes(\"2\");\nactive_days[3] = blynk_day_data.includes(\"3\");\nactive_days[4] = blynk_day_data.includes(\"4\");\nactive_days[5] = blynk_day_data.includes(\"5\");\nactive_days[6] = blynk_day_data.includes(\"6\");\nactive_days[7] = blynk_day_data.includes(\"7\");\n\n// get the start and stop times (in seconds since midnight)\nvar start_time = msg.arrayOfValues[0];\nvar stop_time = msg.arrayOfValues[1];\n\n// If the stop time is earlier than the start time then the stop time is tomorrow\n// example - start time = 8pm, stop time = 6am - stop time needs to be the following day\nif (stop_time < start_time)\n{\n    var is_stop_time_tomorrow = true;  \n}\nelse\n{\n    var is_stop_time_tomorrow = false;\n}\n\n// Store these values in flow variables for this timer...\nflow.set('active_days_timer_1', active_days);\nflow.set('start_time_timer_1', start_time);\nflow.set('stop_time_timer_1', stop_time);\nflow.set('is_stop_time_tomorrow_timer_1', is_stop_time_tomorrow);","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":60,"wires":[]},{"id":"ee6f988209c5f46e","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":700,"wires":[["0ed39756252f0a60"]]},{"id":"fcf199660e10e3a5","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_2 Time Input Widget","pin":"2","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":120,"wires":[["b91589e4e67ffb00"]]},{"id":"b91589e4e67ffb00","type":"function","z":"d3992c1c4a1f414d","name":"Store On/Off Times etc for Timer_2","func":"// get the partial array of days from Blynk\nvar blynk_day_data = msg.arrayOfValues[3];\nvar active_days = [`dummy`,true,true,true,true,true,true,true];\n\nactive_days[1] = blynk_day_data.includes(\"1\");\nactive_days[2] = blynk_day_data.includes(\"2\");\nactive_days[3] = blynk_day_data.includes(\"3\");\nactive_days[4] = blynk_day_data.includes(\"4\");\nactive_days[5] = blynk_day_data.includes(\"5\");\nactive_days[6] = blynk_day_data.includes(\"6\");\nactive_days[7] = blynk_day_data.includes(\"7\");\n\n// get the start and stop times (in seconds since midnight)\nvar start_time = msg.arrayOfValues[0];\nvar stop_time = msg.arrayOfValues[1];\n\n// If the stop time is earlier than the start time then the stop time is tomorrow\n// example - start time = 8pm, stop time = 6am - stop time needs to be the following day\nif (stop_time < start_time)\n{\n    var is_stop_time_tomorrow = true;  \n}\nelse\n{\n    var is_stop_time_tomorrow = false;\n}\n\n// Store these values in flow variables for this timer...\nflow.set('active_days_timer_2', active_days);\nflow.set('start_time_timer_2', start_time);\nflow.set('stop_time_timer_2', stop_time);\nflow.set('is_stop_time_tomorrow_timer_2', is_stop_time_tomorrow);","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":120,"wires":[]},{"id":"776f392f26025355","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"2","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":120,"wires":[]},{"id":"dcc3b2bc65358a82","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":760,"wires":[["0f1f11cbe8de8337"]]},{"id":"0f1f11cbe8de8337","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":720,"y":760,"wires":[]},{"id":"9e4442787ccd2ca2","type":"blynk-iot-client","name":"Node-Red_Testbed","path":"blynk.cloud","key":"xxxxxxxxxxx-xxxxxxxx","tmpl":"TMPLxxxxxxxx","dbg_all":false,"dbg_log":false,"dbg_prop":false,"dbg_sync":false,"dbg_low":false,"dbg_pins":"","multi_cmd":false,"enabled":true}]

You’ll need to edit the Blynk IoT nodes to tell them to use your connection
This expects time input widgets on pins V1 and V2

It should be fairly self-explanatory to add-in more timers.

As far as only activating lights if it’s dark, or some other criteria such as you being at home, it’s fairly straightforward. In my home automation setup in Spain I have a weather station that includes a light level sensor. This outputs an MQTT message every 5 seconds with the current brightness value (mapped to a range of 100 to 0).
I have a node that looks at this data and if the brightness value is 2% or less then it sets a global data value (variable) called isDark to “true”, otherwise it’s “false”…

image

I then use this value in a flow that is connected to an RFID reader which allows access to my front gate. If I present a valid RFID fob to the gate then the gate opens, and if the global isDark flag is “true” then the outside lights that cover that area turn on for two minutes.

You could use the same principal in your flows, checking if it’s dark before deciding whether to turn lights on etc.

Pete.

Thank you very much for this first draft! I will try to build this out further and let you know.

One question: I added my Blynk-token data in the 4 Blynk nodes of your flow and redeployed. When changing the time inputs on these virtual pins I become the new settings in Nodered (I checked this by adding a debug node), however when reaching the moment the time of the input is there, I don’t get results from the timer debug nodes. The "Process-timers"node is injected every second and the result of that node gives “msg.payload: undefined” every second and stays undefined also when the timer should change.

Is this in a situation where the current day isn’t an active day?

I can reproduce that behaviour in that situation, but before I fix the issue I need to know if there’s another scenario I’m missing. If you’re getting this result in a different scenario then you need to let me know what the scenario is.

Pete.

Hi Pete,

I added Debug node 2 after Timer_1 input widget node, and Debug 3 node after Timer_2 input widget node.

After deploying I got the first three parts and after setting time input widget V1 starting on 18:37:10 all days I got the last part. After this nothing follows anymore. Can it be a difference because of time zones maybe?

Here are my settings:

Your screenshots don’t show the “msg.payload: undefined” message you mentioned earlier.
Instead you seem to be saying that you didn’t get an it out value of “1” at 18:37:10 followed a “0” at 18:38:10
Is that correct?

It would really help if you explained in detail the settings AND the issue you are experiencing.

What does your flow context data show…

You need to hit the refresh button for the values to show/update.

Pete.

That is correct, nothing happened.

I got the undefined every second by putting a Debug-node after the “Process-Timers function”. I erased that one again as it keeps going every second, but the result was as follows:

image

…which might be expectable I guess.

My context-data is as follows:

That seems to be okay I think?

Your timer 2 active days are all “false”.

Are the screenshots of your 2nd timer?
What virtual pin is it connected to?
If it’s not Virtual Pin 2 then have you changed both the “Timer_2 Time Input Widget” node and the “Pin V2 sync” nodes so that they are connected to the correct pin (not just having the correct pin in the description)?

Pete.

Timer 1 is on pin V1 and Timer 2 on pin V2.

However, something new occured to me: the timers take action exactly one hour later, so I think there is some kind of a timezone thing. What is the 3600 s in the arrayOfValues[4] meant for?

Im trying my best to help you here, but if you don’t answer my questions…

it’s impossible for me to understand what’s happening at your end.

It’s 1 hour represented as minutes.

If you go to Web Console > Device Info > Metadata > Device Timezone what value do you see here?

If you go to the app and edit the time input widget to enable timezone, does this change the values that you see in the Node-Red Configuration screen (you may need to alter the time in the time input widget (maybe by 1 second) for it to output updated values. You also need to refresh the flow data in the configuration data screen).

Pete.

The screenshots were of the 2nd timer I think.

I think it’s quite working, but there is a shift of 1 hour:

(I don’t know why pin 2 was not activated here (on 65780), it worked when I tried again)

I think you mean the Blynk console here? There is no Timezone information in the Device Info, nor in the Metadata tab. But the time of starting the Blynk app which is represented there says there is no shift in timezone, it’s the same time as it is here.

When choosing the option to be able to change timezones in the Blynk time input, the output file gets different on point no. 4. It is 3600 s here in the Netherlands, and gets 0 on GMT. However, this setting does not affect the timers. They keep getting in action 1 hour later as the time input was set, independent on the timezone chosen.

(66790 for switching off = 18:33:10 and the action took place at 19:33:09):
image

That’s not really a great basis for me to start investigating the issue, is it?

Yes, accessed via the web browser, not the app.

I don’t understand this statement.

I don’t understand that statement either.

What type of device are you running Node-Red on, and what timezone is it set to?

I’m really struggling to help you with this, because of the conflicting data that you’re providing, and strange statements that I can’t understand.
I’d suggest that you explain things in more detail, and take more of a logical and forensic approach to testing so that I can more clearly understand what issues you are facing, and how I might be able to recreate them myself.

Pete.

I’m sorry, I agree, but I cannot see anymore where exactly I got it.

image

This is what the device time was on the Blynk console after starting up the Wemos at 19:10 here, so I thought there is no shift in timezones at this place.

Following are the results after setting the Timezone of the time input on GMT time and timezone Europe/Amsterdam respectively. No. 4 of the array data gives there 0 s and 3600 s respectively. Changing this did not affect the time the actions because of the time inputs took place. They are always happening one hour after the set time.

Nodered is running on an RPI. When I start up Putty and type the date command, the time of the RPI occured te be one hour earlier than the real time here, so I changed the timezone of the RPI to Europe/Amsterdam. This does not help: there is still one hour difference between the time of the time input and the time the action takes place. The time given in the debug window of Nodered give the real times without difference.

I’m aware I’m writing this down a bit clunsy. I’m not used to doing these things, nor writing in English. And I’m just writing down a lot of my observations as I don’t know if the information may be valueable.

Actually your setup works for the most, except for one thing: when I program the time inputs on an arbitrary time, the actions always take place one hour later, independent of the time zone settings of the time input widget, and even after setting the correct RPI time / timezone. The payload data in the debug window give the right seconds after midnight however.

This sounds like it might have something to do with the issue.
Have you tried rebooting the Pi and re-checking the timezone, then repeating the tests?

Following your comments here…

I am aware of a problem with the logic of my code relating to unselected days, but before I attempt to fix this I want to fully understand the other issues.

Pete.

I did now. The timezone on the RPI stays correct after rebooting and the timers react on the right times, so without 1 hour delay! So that means this part works. Thank you! :grinning:

I get the "undefined"payload once a second when adding a debug node after the function of “Process Timers”. I think that might be expectabl and may not be very interesting.
image

So I understand you found a problem in your code with the selected days? What is that problem?

Something I found coincidental with testing was following: on timer 2 no days were selected, Time on = 7:17:25, time off = 7:17:34. Acctivation of the timer did not take place, but the deactivation did.
image
However, I tried to reproduce this a few minutes later, but nothing happened then. Maybe the problem you found has to do with this?

1 Like

As I said this code for Blynk timer handling in Nodered workes well, but now I got a little problem:

I wanted to change the place of the RPI this morning and therefore it was out of electricity for a while. After restarting I get the following error in Nodered every second:

image

The RPI is on the same Wifi-network as before and I did not change the Nodered-nodes.

Do you know why I get this and how I should handle it?

I’ve been doing some more testing this week and realised that there are some other problems with my initial code, which was pieced-together from something I’d used for a while, but which didn’t get fully tested in different scenarios.

I’ve been doing some work on re-writing it using a totally different approach - which makes it much neater and easier to test. I’ve got a few bugs to iron-out then I need to do more testing, so give me a few days.

Pete.

Oh, thats great! I was afraid you did not answer anymore because you got sick of my dumb questions all the time :sweat_smile:

I studied a bit the way to setup code for the function node in Nodered, but it’s still very difficult for me. It’s a big changeover for me having only a littlebit C++ knowledge from a few schoollessons 30 years ago…

No problem, take your time.

Carl

Okay, here is the revised flow.
It’s much easier to expand to handle more inputs without the need to duplicate code, because it simply calls the same function for each timer.
You just need to add-in the additional nodes for the additional timers, and make two changes to the code in the “Process Timers” node - lines 1 and 55 as explained in the “Instructions” node.
You then just need to edit the node so that it has more outputs.

[{"id":"d3992c1c4a1f414d","type":"tab","label":"Time Input Widget Testing","disabled":false,"info":"","env":[]},{"id":"ed80c198c81ac38a","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_1 Time Input Widget","pin":"1","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":60,"wires":[["25f64907850fe98c"]]},{"id":"c1ec2fa4500956b4","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"1","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":60,"wires":[]},{"id":"e6507dab04dc22a7","type":"inject","z":"d3992c1c4a1f414d","name":"Sync at startup","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"0.5","topic":"","payload":"","payloadType":"date","x":1020,"y":60,"wires":[["c1ec2fa4500956b4","776f392f26025355","0118c64fdce2bcc5","5afc36afa9031112"]]},{"id":"3e69839b2d9e3ea2","type":"function","z":"d3992c1c4a1f414d","name":"Process Timers","func":"var pins_used = [1, 2, 27, 34] // Manually add the virtual pin numbers that have time input widgets attached to them\n\n\n// --- You shouldn't need to change anything below here except Line 55 where each timer should have a matching output ---\n\n// This function uses an array of flow data for each pin which has a time input widget attached.\n// The data is structured as follows...\n//\n// pin_number[0]    start time in seconds since midnight\n// pin_number[1]    stop time in seconds since midnight   \n// pin_number[2]    tz description - not used\n// pin_number[3]    Array of active days active_days = Sunday = 0, Saturday = 6\n// pin_number[4]    tz offset - not used\n\n\n\n// Calculate the current day number (day_number)\nvar date = new Date();\nvar day_number = date.getDay(); // Sunday = 0, Saturday = 6\n\n\n// Calculate the current seconds since midnight (current_seconds_since_midnight)\nvar current_hour = date.getHours();\nvar current_minute = date.getMinutes();\nvar current_seconds = date.getSeconds();\nvar current_seconds_since_midnight = (current_hour * 3600) + (current_minute * 60) + current_seconds;\n\n// Declare the variables that relate to the outut data...\nvar output_array = []; // array to hold the timer state (on/off) for each timer - used for the node output\nvar output_pointer = 0; // pointer to elements in the output_array \n\n\n// Process each virtual pin / timer...\nfor (var current_pin = 0; current_pin < pins_used.length; current_pin ++)\n{\n    // This loop processes each of the pins used and calls a function to determine whether the timer should be on or off\n    var pin_to_process = pins_used[current_pin]\n    calculate_timer_state(pin_to_process);\n    output_pointer ++; // Increment the pointer\n}\n\n// Perform a check that the node is structured with the same number of outputs as the number of virtual pins / timers we've just processed...\nif (output_pointer != node.outputCount)\n{\n    // We get here if the number of output for this node doesn't match the virtual pins taht we've just processed\n    // so we need to warn the user.\n    // To fix this go to Properties > Setup for this node and check that the number of outputs matches\n    // the number of entries in the 'var pins_used' array on line 2 of this code.\n    // Also ensure that the \"return...\" line below (line 55) has the correct number of entries as well.\n    node.warn(\"THE NUMBER OF OUTPUTS DOES NOT MATCH THE NUMBER OF VIRTUAL PINS PROCESSED IN THE'PROCESS TIMERS' FUNCTION\");\n}\n\n// Return the results.\n// Please ensure that the node has the correct number of outputs, and that there is an entry in this list for each output...\nreturn [{payload: output_array[0]}, {payload: output_array[1]}, {payload: output_array[2]}, {payload: output_array[3]}];\n\n\n\n\n// Function that we call once for each virtual pin / timer which calculates whether the output should be on or off... \nfunction calculate_timer_state(pin)\n{\n    //node.warn(\"Processing timer attached to virtual pin \" + pin); // enable for debugging\n\n    // Retreive the stop/start and actve days data for this timer...\n    var start_time = flow.get(pin + \"[0]\");\n    var stop_time = flow.get(pin + \"[1]\");\n    var active_days = flow.get(pin + \"[3]\");       // retreive the array of active days for this timer   \n\n    // Check if today is an active day for this timer\n    var is_today_active = active_days[day_number]; \n    //node.warn(\"is_today_active = \" + is_today_active); // enable for debugging\n\n\n\n    // There are 3 scenarios that can result in the output of this timer being ON...\n\n    // 1) The timer's stop time is after it's start time, today is an active day and the current time is between the start and stop times\n    //    This is a \"normal\" timer where it starts and stops in the same day, and should be on if the current time is between the star/stop times\n\n\n\n    // 2) The timer's stop time earlier than it's start time, and yesterday was an active day, and the current time is before the stop time\n    //    This means that the output was turned-on yesterday, and the timer should stay on until the stop time rolls around today, then go off.\n    //    This is regardles of whether today is an active day, the important factor is that yesterday (when the timer turned ON) was an active day.\n    //    (If today is not an active day then the timer won't be turned on atain later today)\n    //\n    //    (Example - today is Tuesday. Monday was an active day and the start time is 8pm and stop time is 8am.\n    //    Timer was activated on Monday at 8pm and ges off today (Tuesday) at 8am. \n    //    If the current time is 7:59 am on Tuesday the the timer shoulod still be on)\n\n\n    // 3) The timer's stop time is earlier than it's start time, today is an active day and the current time is after the start time\n    //    This means that the timer turns on today, but will turn off tomorrow. \n    //\n    //    (Example  - today is Tuesday and an active day. The timer has a start time of 8pm and a stop time of 8am (on Wednesday)\n    //     The current time is 8:01pm (20:01 hrs) so the timer should be on, and remain on until 8am tomorrow)\n \n\n\n    // Handle Scenario 1\n    if ((start_time < stop_time) && is_today_active && (current_seconds_since_midnight >= start_time) && (current_seconds_since_midnight < stop_time))\n     {\n        //node.warn(\"Timer set to ON in Scenario 1\"); // enable for debugging\n        output_array[output_pointer] = 1; // the current timer output wiil be ON (1)\n        return;\n    }\n\n\n    // Handle Scenarios 2 & 3\n    if (stop_time < start_time)\n    {\n        // handle scenario 3 first, it's easier...\n        if(current_seconds_since_midnight >= start_time)\n        {\n            if(is_today_active)\n            {\n                // We get here if the start time was earlier than thE stop time, but it's now past the start time\n                // and tofday is an active day - so the output should be ON\n                //node.warn(\"Timer set to ON in Scenario 3\"); // enable for debugging\n                output_array[output_pointer] = 1; // the current timer output wiil be on (1)\n                return;                \n            }\n        }\n    else\n    {\n        // handle sceario 2\n        if(current_seconds_since_midnight < stop_time)\n        {\n            // We need to work-out if the output turned-on yesterday.\n            // We start by working-out the day number yesterday, then seeing if it weas active...\n            var yesterday_day_num = day_number - 1\n            if (yesterday_day_num == -1)\n            {\n                yesterday_day_num = 6;\n            }\n\n            var was_yesterday_active = active_days[yesterday_day_num];\n            // node.warn(\"Scenario 2 - was_yesterday_active = \" + was_yesterday_active); // enable for debugging\n\n            if (was_yesterday_active)\n            {\n                // We get here if yesterday was an active day, and the timer started yesterday, and needs to stop today\n                // and the current time is earlier than the stop time - so the output should be ON\n                //node.warn(\"Timer set to ON in Scenario 2\"); // enable for debugging\n                output_array[output_pointer] = 1; // the current timer output wiil be ON (1)\n                return; \n                }\n            }\n\n        }\n    }    \n\n\n    // There are 6 scenarios that can result in the output of this timer being OFF...\n    // Note that we don't get here if any of the \"ON\" scenarios were true\n\n    // A) The timer's start and stop times are the same.\n\n\n    // B) The timer's start time is earlier than the stop time and the current time is either before ths start time or after the stop time\n    //    This is a \"normal\" timer where it starts and stops in the same day, and should be OFF if the current time is before the start time or after the stop time\n\n\n    // C) The timer's start time is earlier than the stop time and the current time is between the start and stop times, but today is not an active day\n    //    This is also a \"normal\" timer where it starts and stops in the same day, and should be OFF if the timer is between the start and stop times,\n    //    but today is not an active day.\n\n\n    // D) The timer's stop time is earlier than the start time, but the current time is after the stop time but before the start time\n    //    This is a timer where the timer started yesterday and should now be off because the stop time has been reached or exceeded, \n    //    but we've not yet reached the start time.\n\n    // E) The timer's stop time is earlier than the start time, but the current time is after the start time but today is not an active day\n    //    This is a timer where the timer would normally start today and end tomorrow, and we've reached or exceeded the start time,\n    //    but because today is not an active day it should be OFF\n\n\n    // F) The timer's stop time is earlier than the start time, and the current time is before the stop time, but ysterday wasn't an active day\n    //    This is a timer that would have started yesterday and should still be on, because the stop time has not yet been reached,\n    //    but because yesterday was not an active day the timer would not have stated, so should be OFF   \n\n\n    // Handle Scenario  A\n    if (start_time == stop_time) \n    {\n        //node.warn(\"Timer set to OFF in Scenario A\"); // enable for debugging\n        output_array[output_pointer] = 0; // the current timer output wiil be OFF(0)\n        return;\n    }\n\n\n    // Handle Scenario B\n    if((start_time < stop_time) && (current_seconds_since_midnight < start_time || current_seconds_since_midnight >= stop_time))\n    {\n        //node.warn(\"Timer set to OFF in Scenario B\"); // enable for debugging\n        output_array[output_pointer] = 0; // the current timer output wiil be OFF (0)\n        return;\n    }\n\n\n    // Handle Scenario C\n    if((start_time < stop_time) && (current_seconds_since_midnight >= start_time || current_seconds_since_midnight <= stop_time) && (!is_today_active))\n    {\n        //node.warn(\"Timer set to OFF in Scenario C\"); // enable for debugging\n        output_array[output_pointer] = 0; // the current timer output wiil be OFF (0)\n        return;       \n    }\n\n\n    // Handle Scenario D\n    if((stop_time < start_time) && (current_seconds_since_midnight >= stop_time) && (current_seconds_since_midnight <= start_time))\n    {\n        //node.warn(\"Timer set to OFF in Scenario D\"); // enable for debugging\n        output_array[output_pointer] = 0; // the current timer output wiil be OFF (0)\n        return;          \n    }\n\n\n    // Handle Scenario E\n    if ((stop_time < start_time) && (current_seconds_since_midnight >= start_time) && (!is_today_active))\n    {\n        //node.warn(\"Timer set to OFF in Scenario E\"); // enable for debugging\n        output_array[output_pointer] = 0; // the current timer output will be OFF (0)\n        return;            \n    }   \n\n\n    // Handle Scenario F\n    if((stop_time < start_time) && current_seconds_since_midnight < stop_time)\n    {\n        // We need to work-out if the output turned-on yesterday.\n        // We start by working-out the day number yesterday, then seeing if it was active...\n        var yesterday_day_num = day_number - 1\n        if (yesterday_day_num == -1)\n        {\n            yesterday_day_num = 6;\n        }\n\n        var was_yesterday_active = active_days[yesterday_day_num];\n        //node.warn(\"Scenario F - was_yesterday_active = \" + was_yesterday_active); // enable for debugging\n\n        if (!was_yesterday_active)\n         {\n            // We get here if yesterday was an not an active day, so the timer didn't start yesterday, and  therefore should be off\n            // and the current time is earlier than the stop time - so the output should be ON \n            //node.warn(\"Timer set to OFF in Scenario F\"); // enable for debugging\n            output_array[output_pointer] = 0; // the current timer output will be OFF(0)\n            return;\n        }\n    }\n\n\n\n    // we get here if none of the scenarios above were met - warn the user...\n    node.warn(\"UNEXPECTED SITUATION OCCURED IN 'PROCESS TIMERS' FUNCTION\");\n}","outputs":4,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":600,"wires":[["ea72497c3ecf1a6a"],["dd33edfc36c49718"],["869a6f4d76223d98"],["579220185a1188f8"]],"inputLabels":["Trigger"],"outputLabels":["Timer_1","Timer_2","Timer_3","Timer_4"]},{"id":"0ed39756252f0a60","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":700,"y":540,"wires":[]},{"id":"9ceebf4490b1865a","type":"inject","z":"d3992c1c4a1f414d","name":"Repeat ever second","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":"3","topic":"","payload":"","payloadType":"date","x":140,"y":600,"wires":[["3e69839b2d9e3ea2"]]},{"id":"fcf199660e10e3a5","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_2 Time Input Widget","pin":"2","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":120,"wires":[["25f64907850fe98c"]]},{"id":"776f392f26025355","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"2","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":120,"wires":[]},{"id":"0f1f11cbe8de8337","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":700,"y":580,"wires":[]},{"id":"25f64907850fe98c","type":"function","z":"d3992c1c4a1f414d","name":"Store Timer Values","func":"var virtual_pin_number = msg.pin // we use this to identify which virtual pin we are using\n\n// get the start and stop times (in seconds since midnight)\nvar start_time = parseInt(msg.arrayOfValues[0]);\nvar stop_time = parseInt(msg.arrayOfValues[1]);\n\n// Handling of sunrise or sunset not supported by this flow\n// warn the user if these are detected...\nif (isNaN(start_time) || isNaN(stop_time))\n{\n    node.warn(\"Sunrise or Sunset used for timer attached to virtual pin \" + virtual_pin_number);\n}\n\n\n// get the timezone info (not used)\nvar tz_description = msg.arrayOfValues[2];\nvar tz_offset = parseInt(msg.arrayOfValues[4]);\n\n// get the partial array of days from Blynk\nvar blynk_day_data = msg.arrayOfValues[3];\nvar active_days = [];\n\n// Re-map Blynk days (Mon = 1, Sun = 7) to NodeJS days (Sun = 0, Mon = 6) and populate an array with true/false for each day...\nactive_days[0] = blynk_day_data.includes(\"7\");\nactive_days[1] = blynk_day_data.includes(\"1\");\nactive_days[2] = blynk_day_data.includes(\"2\");\nactive_days[3] = blynk_day_data.includes(\"3\");\nactive_days[4] = blynk_day_data.includes(\"4\");\nactive_days[5] = blynk_day_data.includes(\"5\");\nactive_days[6] = blynk_day_data.includes(\"6\");\n\n// Save the results...\nflow.set(virtual_pin_number + \"[0]\", start_time);\nflow.set(virtual_pin_number + \"[1]\", stop_time);\nflow.set(virtual_pin_number + \"[2]\", tz_description);\nflow.set(virtual_pin_number + \"[3]\", active_days);\nflow.set(virtual_pin_number + \"[4]\", tz_offset);\n\nreturn msg;","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":240,"wires":[]},{"id":"2048fe9c051cca4a","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_3 Time Input Widget","pin":"27","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":180,"wires":[["25f64907850fe98c"]]},{"id":"9ac8c93bda2da52a","type":"blynk-iot-in-write","z":"d3992c1c4a1f414d","name":"Timer_4 Time Input Widget","pin":"34","pin_all":0,"client":"9e4442787ccd2ca2","x":170,"y":240,"wires":[["25f64907850fe98c"]]},{"id":"0118c64fdce2bcc5","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"27","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":180,"wires":[]},{"id":"5afc36afa9031112","type":"blynk-iot-out-sync","z":"d3992c1c4a1f414d","name":"","pin":"34","pinmode":0,"client":"9e4442787ccd2ca2","x":1260,"y":240,"wires":[]},{"id":"983df734590070e6","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":700,"y":620,"wires":[]},{"id":"312ec27cbd2e7138","type":"debug","z":"d3992c1c4a1f414d","name":"Timer_4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":700,"y":660,"wires":[]},{"id":"ea72497c3ecf1a6a","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":540,"wires":[["0ed39756252f0a60"]]},{"id":"dd33edfc36c49718","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":580,"wires":[["0f1f11cbe8de8337"]]},{"id":"869a6f4d76223d98","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":620,"wires":[["983df734590070e6"]]},{"id":"579220185a1188f8","type":"rbe","z":"d3992c1c4a1f414d","name":"","func":"rbe","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":570,"y":660,"wires":[["312ec27cbd2e7138"]]},{"id":"7ace0bb4b3f31a72","type":"comment","z":"d3992c1c4a1f414d","name":"Instructions","info":"Currently set-up with 4 Time Input widgets.\nTo add more, do the following...\n\nCreate the String virtual datastreams in Blynk\nAdd additional Write Event nodes connected to these datastreams, that feed in to the \"Store Timer Values\" function\nAdd additional Sync nodes connected to these datastreams, that are triggered by the \"Sync at Startup\" nodes\nEdit the \"Process Timers\" function as follows...\n   Edit Line 1 to list the virtual pins that have Time Input widgets attached to them\n   (the sequence of these entries will determine the sequence of the data that is outputted from the function)\n   \n   Edit line 55 to ensure that there is an entry for each of the time input widgets (numbered consecutively, starting at zero)\n   \n   Click the Setup tab and ensure that the number of outputs matches the number of Time Input widgets in use\n\n   Click the \"Appearance\" icon and ensure that each output has a meaningful name\n\n\nNOTES - This flow has not been written to handle Sunrise and Sunset selection in the Time Input widget.\n\n      - Timezone corrections are not used, so it is recommended not to enable the use of time zone selection in the Time Input widgets.\n      ","x":750,"y":260,"wires":[]},{"id":"9e4442787ccd2ca2","type":"blynk-iot-client","name":"Node-Red_Testbed","path":"blynk.cloud","key":"xxxxxxxxxxx-xxxxxxxxx","tmpl":"TMPLDPkisrgH","dbg_all":false,"dbg_log":false,"dbg_prop":false,"dbg_sync":false,"dbg_low":false,"dbg_pins":"","multi_cmd":false,"enabled":true}]

I’ve tested all of the scenarios I can think of and they work fine, but if you identify any issues then you’ll need to document the exact scenario that caused this (Start and stop times, current time, active days etc) if you want me to take a look at the issue.

Pete.