The DELAY node. Not doing what I want

There’s something wrong with something.

Indulge me:

[{"id":"2e93a8b5.1f22d8","type":"inject","z":"e9882c4e.450a6","name":"","topic":"","payload":"Banana","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":640,"wires":[["920c54cd.f416b"]]},{"id":"920c54cd.f416b","type":"function","z":"e9882c4e.450a6","name":"Fast track","func":"//          var msg2 = { payload:\"N/C\" };\nmsg = {payload:\"Payload\", delay:1000};\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":640,"wires":[["b9ea90e8.3238e8"]]},{"id":"8755038.67b138","type":"inject","z":"e9882c4e.450a6","name":"","topic":"","payload":"Banana","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":740,"wires":[["2687224f.5a0a9e"]]},{"id":"33b2cea6.90bb22","type":"inject","z":"e9882c4e.450a6","name":"","topic":"delay","payload":"1000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":300,"y":840,"wires":[["e62fa1f0.d7b41","e7efcbb9.3d718","2687224f.5a0a9e"]]},{"id":"2687224f.5a0a9e","type":"function","z":"e9882c4e.450a6","name":"","func":"context.delay_ = msg.topic;\ncontext.msg_ = msg.payload;\n//if (context.msg_ > 0)\nif (msg.payload > 0)\n{\n    context.set('delay_',msg.payload);\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"value\"});\n    return msg.delay;\n}\nelse\n{\n//    msg.payload = context.msg_;\n//    msg.delay = context.delay_;\n//    msg = {payload:msg.payload, delay:msg.delay};\n    msg = {payload:context.msg_, delay:context.delay_};\n    node.status({fill:\"green\",shape:\"dot\",text:\"message\"});\n    return msg;\n}\n","outputs":1,"noerr":0,"x":690,"y":740,"wires":[["b9ea90e8.3238e8","7f916ac2.ae7bcc","eaebfbfa.76676"]]},{"id":"b9ea90e8.3238e8","type":"delay","z":"e9882c4e.450a6","name":"","pauseType":"delayv","timeout":"6","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":900,"y":740,"wires":[["522c2349.6c1c3c"]]},{"id":"522c2349.6c1c3c","type":"debug","z":"e9882c4e.450a6","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1070,"y":740,"wires":[]}]

Press the top “banana” node. That goes through a “fast track” node. This sets the time to 1000 ms.

That goes down to the delay node. That shows 1 second. It works.

Given that it has 1 second now, if you press the delay 1000 button (ok, you will see the yellow indicator activate on the function node) and then the lower “banana” button, you will see the green indicator.
The delay node becomes active, but it is 6 seconds.

Sorry, but that goes against what you said previously:
That should not be the case. Once a delay and value message have been received then each value message should be delayed by the time specified by the latest delay message.

Blank look.

Oh, just to declare:
Yes, I know: in the latter example, the “fast track” makes it 1 second.
Pressing the button marked 1000 (further down) also sets it to 1 second.
But when you press the banana button and it goes through the function node I wrote, it somehow becomes 6 seconds…

Just so it can’t be said I was being silly using the same value twice. Irrispective, with my function block it somehow becomes a 6 second delay.

It does require one of each before the first message will be sent out, but only the first time. Thereafter each value message will be delayed by the time from the most recent delay message. I assumed that the slider will send a message on startup so that would be ok. If it doesn't then add an inject node set to inject once on startup with the default delay time you want. So on my sample flow just change one of the delay injectors to inject on startup.

If you want to use a function you don’t need anything as complex as you have. Something like this should do it

[{"id":"3edc9f5b.5cfea","type":"inject","z":"6dc690a3.1abc88","name":"","topic":"delay","payload":"2000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":683,"wires":[["33116ffe.769ba8"]]},{"id":"a87e4f8f.a642d8","type":"inject","z":"6dc690a3.1abc88","name":"","topic":"","payload":"Banana","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":104,"y":797,"wires":[["33116ffe.769ba8"]]},{"id":"88eab954.f85478","type":"inject","z":"6dc690a3.1abc88","name":"","topic":"delay","payload":"3000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":101,"y":721,"wires":[["33116ffe.769ba8"]]},{"id":"eefd1ddd.b713b","type":"debug","z":"6dc690a3.1abc88","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":584,"y":649,"wires":[]},{"id":"33116ffe.769ba8","type":"function","z":"6dc690a3.1abc88","name":"Set delay","func":"if (msg.topic === \"delay\") {\n    context.set(\"delay\", msg.payload);\n    msg = null;\n} else {\n    delay = context.get(\"delay\");\n    if (delay === undefined) delay = 1000;  // default delay ms\n    msg.delay = delay;\n}\nreturn msg;","outputs":1,"noerr":0,"x":303,"y":744,"wires":[["78252539.d7ae4c"]]},{"id":"78252539.d7ae4c","type":"delay","z":"6dc690a3.1abc88","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":486,"y":746,"wires":[["81ccddb.13174a"]]},{"id":"81ccddb.13174a","type":"debug","z":"6dc690a3.1abc88","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":647,"y":746,"wires":[]}]

Thanks Colin.

While we were both working on the “problem”, I got this:

Excuse the mess. But it works.

I wouldn’t mind getting rid of the extra junk in the function block, but that’s work in progress.

[{"id":"522c2349.6c1c3c","type":"debug","z":"e9882c4e.450a6","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1070,"y":740,"wires":[]},{"id":"b9ea90e8.3238e8","type":"delay","z":"e9882c4e.450a6","name":"","pauseType":"delayv","timeout":"6","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":900,"y":740,"wires":[["522c2349.6c1c3c"]]},{"id":"2687224f.5a0a9e","type":"function","z":"e9882c4e.450a6","name":"","func":"var delay_ = msg.delay;\ncontext.payload_ = msg.payload;\ncontext.topic_ = msg.topic;\nif (context.topic_ == 'delay')\n{\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"value\"});\n    context.set('delay_',msg.payload);\n}\nelse\n{\n    node.status({fill:\"green\",shape:\"dot\",text:\"message\"});\n    delay_ = context.get('delay_');\n    msg.delay = delay_;\n    return msg;\n}\n","outputs":1,"noerr":0,"x":690,"y":740,"wires":[["b9ea90e8.3238e8","7f916ac2.ae7bcc","eaebfbfa.76676","eed09f63.06b3c"]]},{"id":"8755038.67b138","type":"inject","z":"e9882c4e.450a6","name":"","topic":"","payload":"Banana","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":370,"y":740,"wires":[["2687224f.5a0a9e"]]},{"id":"33b2cea6.90bb22","type":"inject","z":"e9882c4e.450a6","name":"","topic":"delay","payload":"1000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":300,"y":840,"wires":[["e62fa1f0.d7b41","e7efcbb9.3d718","2687224f.5a0a9e"]]},{"id":"cb7d83ff.37a39","type":"inject","z":"e9882c4e.450a6","name":"","topic":"delay","payload":"9000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":300,"y":900,"wires":[["2687224f.5a0a9e"]]}]

I WILL look at yours out of respect.

Thanks.

Would you mind looking at that last effort?

Questions are pending, but I want to look at your version first.

I like your way better.

I was NEARLY there with what I was doing.

I am still (will I ever?) not confident about the conditional stuff how you did it.

Ok to ask my questions now?
As we seem both to have written the same function in that node.

Yes, we have ended up with similar results, a case of parallel evolution I think. I don’t think the first three lines of your function are necessary, use msg.topic in line 4 instead. Also I am not sure what effect passing an undefined value to the delay node will be (as is the case if you send the first value message before a delay message).
On a point of style I would never put a return anywhere other than at the end, I have seen to many bugs caused by code being added later not being executed because of the early return. However not everyone will agree with me on that one.

1 Like

Great to hear we are similar in our thinking.

I seem to be (still ?) confused with what the context.blah and var things.

My thinking is:
(excuse the rant)
The message comes in.
The delay is in msg.delay
If the msg.delay is “valid” store this. From what I get this has to be via CONTEXT, as it has to survive the node’s reiteration.

Alas this is done by looking at the msg.topic and if it equals ‘delay’ then…

ELSE
(meaning that it wasn’t a delay message)
get the stored value and construct the new message.
That is done by getting this message’s payload and add the msg.delay to it.

I have the first three lines because I need to get the topic, payload (to store if it is a delay) and …
You are probably right. I shall delete them.

There seems to be a flaw with my structural understanding of what is what.

Again: Not wanting to dismiss your efforts, this is the boiled down version I got with a mixture of what you have and what I had.

Now, also there are a few things which are “different” to the original request and were left out only to keep it simple.

I haven’t included the extra input nodes as this is only to get the idea working.
Oh, the idea:
A message is received and a .sh (bash) script is run to then execute an .py (python) script.
This gets around the problem about parsing the message to the script. (another thread.)

It is displayed and after a certain (set) time, it is wiped.
It is wiped by running another .sh script. Which is where the delay comes in.
So please don’t fret about what the scripts do and how they do it.
I busted a lot of time getting them to work and take multiple words as parameters.
This is done by wrapping them in “quotes”.

[{"id":"2dfc598e.d9a4ee","type":"ui_slider","z":"e9882c4e.450a6","name":"","label":"Wipe Delay","group":"d8f7708b.e6a3b","order":0,"width":"5","height":"1","passthru":true,"topic":"delay","min":0,"max":"20000","step":"1000","x":110,"y":180,"wires":[["d6716a43.917c88","e4ae8879.587a6","e9bf5a2f.345b3","ffec291e.484cc"]]},{"id":"4851ca4d.bbe0dc","type":"inject","z":"e9882c4e.450a6","name":"","topic":"","payload":"Banana","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":220,"wires":[["9f112d7f.e7a9d8","e9bf5a2f.345b3"]]},{"id":"e9bf5a2f.345b3","type":"function","z":"e9882c4e.450a6","name":"Set delay","func":"if (msg.topic === \"delay\") \n{\n    node.status({fill:\"yellow\",shape:\"dot\",text:\"value\"});\n    context.set(\"delay\", msg.payload);\n//    msg = null;\n} \nelse \n{\n    node.status({fill:\"green\",shape:\"dot\",text:\"message\"});\n    delay = context.get(\"delay\");\n    if (delay === undefined) delay = 1000;  // default delay ms\n    msg.delay = delay;\n    return msg;\n}\n","outputs":1,"noerr":0,"x":300,"y":220,"wires":[["315aeaa0.3c51ce","11f395d8.d4035a","f2b49294.ac6ce8"]]},{"id":"ffec291e.484cc","type":"function","z":"e9882c4e.450a6","name":"/1000","func":"var j = msg.payload;\nvar x = j / 1000;\nmsg.payload = x;\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":180,"wires":[["cf4017e3.973d7"]]},{"id":"6ecb508b.ca1f1","type":"inject","z":"e9882c4e.450a6","name":"","topic":"delay","payload":"4000","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":"2","x":110,"y":280,"wires":[["e9bf5a2f.345b3"]]},{"id":"cf4017e3.973d7","type":"ui_text","z":"e9882c4e.450a6","group":"d8f7708b.e6a3b","order":0,"width":"5","height":"1","name":"","label":"Seconds Delay = ","format":"{{msg.payload}}","layout":"row-center","x":490,"y":180,"wires":[]},{"id":"315aeaa0.3c51ce","type":"delay","z":"e9882c4e.450a6","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":460,"y":220,"wires":[["61300481.033ab4"]]},{"id":"9f112d7f.e7a9d8","type":"exec","z":"e9882c4e.450a6","command":"/home/pi/PiFace/LCD_message","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":790,"y":140,"wires":[["d59566e1.636b48","39f43c2a.58068c"],[],[]]},{"id":"61300481.033ab4","type":"exec","z":"e9882c4e.450a6","command":"/home/pi/PiFace/LCD_off","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Wipe display after a time","x":770,"y":220,"wires":[["9c1eb40a.bbea68","39f43c2a.58068c"],[],[]]},{"id":"d8f7708b.e6a3b","type":"ui_group","z":"","name":"Group 1","tab":"cf397e02.acf53","order":1,"disp":true,"width":"10"},{"id":"cf397e02.acf53","type":"ui_tab","z":"","name":"Message CTL","icon":"dashboard","order":3}]

There is also a “default time” node which sets the default time to 4 seconds so when I deploy the flows it isn’t some weird number.

No, it is in msg.payload if msg.topic is delay.

You didn't need the first lines because it set a variable to msg.delay, and msg.delay at this point is not set.
You didn't need the second line because it wrote (using the obsolete context format) to a context variable called 'payload' which you never read back.
You didn't need the third line because you can just use msg.topic directly in the next line.

The flow in your last post looks good, except that you don't need the default delay node, you can set the default in line 11 in the function node which says 'if you haven't already set a delay time in the context then set it to this value'.

Is it all working now?

Yes, and again: Thanks for the help.

Ok, to re-cap:
You did what I did in another case where you sent the value in the payload - given - but tagged the message with the topic. I was mistaken for a long time that it was actually msg.delay which is what the node wants.

But you “cheated” and until the last second it was the topic and you moved the msg.topic -> msg.delay()

Got it.

I am really wanting to get out of using the old context format, but so many of my nodes have that in them and so when I am stuck and search my records I find a lot of them - rather than the new ones which are a lot smaller in occurrence.

Alas I am not doing a good job of documenting the progress of the nodes as they “grow” and become better. (Work in progress).

Ok, the “default time” part. Yes, ok, if the node has a default value. It is really academic as it shouldn’t really ever be in the state where there is no value set. But to be a good programmer, I probably should put it in there.

It will be in the undefined state when you restart node red as the node context will be empty at that point. You should always allow for the case where a context variable may not have been set yet.

And that’s why I put the inject node there to inject a “default” time at start up (or after a DEPLOY) just to tidy things up.

Right?

I’m confused.

As I said previously, the inject node is not necessary because in your function contains the the lines

    delay = context.get("delay");
    if (delay === undefined) delay = 1000;

That code gets the value of delay from the node context, then tests the value provided and if it is undefined (which means you have not yet saved it the first time) then set it to 1000 (which is 1 second). You can change that to whatever time you want the default to be. There is no need for the additional node.

1 Like

Thanks.

Honestly though that was above my skill set.

However I shall apply it asap. I have just spent the past couple of … hours ? chasing a typo’ I made when copy/pasting node data from one to the another and messed up on most.

So some worked and most didn’t. But that is now fixed. Phew!

As an alternative you could use a change node (no function node necessary):

[{"id":"e3e57dbf.e1ccb","type":"change","z":"cf6706ec.415a58","name":"","rules":[{"t":"set","p":"delay","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":600,"wires":[[]]},{"id":"8d1875eb.631a38","type":"inject","z":"cf6706ec.415a58","name":"","topic":"","payload":"3000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":600,"wires":[["e3e57dbf.e1ccb"]]},{"id":"4ae0b364.e31a8c","type":"debug","z":"cf6706ec.415a58","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":630,"y":680,"wires":[]},{"id":"233b0af9.423696","type":"delay","z":"cf6706ec.415a58","name":"","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":480,"y":680,"wires":[["4ae0b364.e31a8c"]]},{"id":"d1edf7f8.1d2398","type":"change","z":"cf6706ec.415a58","name":"","rules":[{"t":"set","p":"delay","pt":"msg","to":"delay","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":680,"wires":[["233b0af9.423696"]]},{"id":"4fa77bc6.9e1f64","type":"inject","z":"cf6706ec.415a58","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":680,"wires":[["d1edf7f8.1d2398"]]},{"id":"fb10915a.47745","type":"inject","z":"cf6706ec.415a58","name":"","topic":"","payload":"4000","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":640,"wires":[["e3e57dbf.e1ccb"]]}]
1 Like

As I said, you should take some time out to learn the basics, it will save you time in the long run.

Just discover something that I think deserves to be pointed… once you set the delay with the msg.delay it doesnt matter anymore what time base you set on the delay node, will works always on miliseconds.

Regards

2 Likes

Trying_to_learn,

It might be helpful to spend a little time to figure out how to manipulate the msg object. this is just my opinion. in general it is less complicated to keep all of the data related to a sequence in the message. this will help avoid conflict that can happen when using any of the external contexts.

i don't understand all of the details of your flow so i will give a general outline of an example.

There is some trigger event that is going to execute a sequence that will set a timer along the way. and, the design is to allow the sequence to fetch the delay duration from a slider. pretty typical use case: user turns on the exhaust fan in the kitchen, which triggers a sequence that will turn on the fan, delay from some amount of time, then turn off the exhaust fan. The fan run time is adjustable using a slider.

when the the exhaust fan is turned on, the first thing the sequence wants to do is fetch the duration from the slider and tuck the value away in the msg object for use later. after your flow uses some kind of node to get the duration from the slider, i am guessing that the duration value will be in msg.payload. you can use a change node to set msg.duration to the value in msg.payload. for the duration of the life of the msg, msg.duration will be available. when you get ready to send the msg to the delay node, use another change node to set msg.delay = msg.duration. presto change-o. when the msg arrives to the delay node, it knows how long to delay. if the msg moves on to another node, msg.duration will still be available. if duration is no longer necessary, another change node can be used to delete msg.duartion.

i too am a newb to node-red. something that i have discovered and don't yet fully understand is the way msg.payload is treated. it seems to me that msg.payload (and all child objects) has some type of protection that prevents its manipulation. (i.e. if msg.payload exists, i don't think i can add anything to it.) so, when i have a flow that uses a node that returns data in msg.payload, i have developed the habit of saving any payload data (just like you will have if you put the msg.payload duration from your slider into msg.duration) then i use a change node to delete msg.payload.

i have several sequences that add as many as 6 different elements to msg (i.e. msg.datathinie1, msg.datathingie2, etc) and those data are available to the sequence for the entire life of the msg.

in the example i have used here, it is possible that the user might turn off the exhaust fan switch before the delay has expired. to deal with that, consider capturing the switch to off state change, use a change node to add msg.reset = true (i think. look at the delay node help to verify), then connect that change node to the same delay node that was used when the switch was turned on.

something else I have not yet verified and that I can't find in the delay node help. it appears to me as though a sequence that sends msg.reset to the delay node terminates at the delay node and will not move to any subsequent nodes beyond the delay. but, i am not completely sure of this. makes sense that it works that way. likewise, if a sequence is delayed, and the delay node received a msg.reset, the original, delayed sequence also seems to terminate at the delay, which does not make sense to me.

hope this helps.

Hi "Athome".

Yeah, node-red is fun.

I've been off-line for a while.

I am not qualified enough to comment on the last part you wrote but:
If you have the DELAY node and send it a RESET message, it is to be expected the message would terminate there.

Suggestion:
Post the flow you have to the forum and see if anyone helps.

I'll give it a look-see as well and see if I can help you.

All the best.

Awesome alternative