Simple settimeout function

Hello everyone,

i´m super happy with my nooby progress on Node-Red but now my almost non existent programming skills come in to effect :frowning:

I´m getting the state of my door sensor from Homebridge (ContactSensorState) and want to return a payload "true" msg if the state stays 1 (open) for longer than 3 seconds. If it gets 0 (closed) within that time i want to return "false".

I tried its by myself for a while but not getting to the goal. I really appreciate any help on that.
Thank you!
Winni

1 Like

This could be done with a trigger node and a switch node
i.e.

[{"id":"32db2f0b.0a5438","type":"inject","z":"8d22ae29.7df6d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"str","x":170,"y":2460,"wires":[["46e061ba.7a4ad"]]},{"id":"ecd34ebb.8522e","type":"inject","z":"8d22ae29.7df6d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"str","x":170,"y":2500,"wires":[["46e061ba.7a4ad"]]},{"id":"46e061ba.7a4ad","type":"switch","z":"8d22ae29.7df6d","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"str"},{"t":"eq","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":2500,"wires":[["86fbffe4.50afb8"],["86fbffe4.50afb8","f23e9056.c5582"]]},{"id":"86fbffe4.50afb8","type":"trigger","z":"8d22ae29.7df6d","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"3","extend":false,"units":"s","reset":"0","bytopic":"all","topic":"topic","outputs":1,"x":460,"y":2500,"wires":[["f23e9056.c5582"]]},{"id":"f23e9056.c5582","type":"change","z":"8d22ae29.7df6d","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"on\": (payload =\"1\" ?  true : false) }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":2480,"wires":[["c7a8c472.018348"]]},{"id":"c7a8c472.018348","type":"debug","z":"8d22ae29.7df6d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":2320,"wires":[]}]
2 Likes

Hello,
No need for a function node and setTimeout.
You can use the build in trigger node for this. It can be set so that it sends the true after 3 seconds but resets and sends nothing if a 0 arrives.
Just add an additional change node to change the 0 to false and it should do what you want:

[{"id":"5c34e9c0.c762","type":"inject","z":"c38d0f4.885dff","name":"Open","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":210,"y":120,"wires":[["dd7c9025.ea66c8"]]},{"id":"c1c9de36.978c98","type":"inject","z":"c38d0f4.885dff","name":"Closed","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":210,"y":180,"wires":[["dd7c9025.ea66c8","288f565b.688202"]]},{"id":"dd7c9025.ea66c8","type":"trigger","z":"c38d0f4.885dff","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"3","extend":false,"overrideDelay":false,"units":"s","reset":"0","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":120,"wires":[["704568b0.5e62d"]]},{"id":"704568b0.5e62d","type":"debug","z":"c38d0f4.885dff","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":120,"wires":[]},{"id":"288f565b.688202","type":"change","z":"c38d0f4.885dff","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":180,"wires":[["704568b0.5e62d"]]}]

Should you want to go down the function node route you would have to wrap your head around a few more concepts.
A function node can only work on the scope of the message it just received. So every time you send a message to a function node this will spawn a new instance of it that doesn’t know about any other instances of the same function.
So to interchange information between different instances of a function node you would have to use context storage:
https://nodered.org/docs/user-guide/context
Than do something similar to the following in your function:

let timer = context.get("timer") || false;
switch(msg.payload){
    case 1:
        if (!timer) {
            timer = setTimeout(()=>{
                context.set("timer", false);
                node.send({payload:true});
            },3000);
            context.set("timer",timer);
        }
        break;
    case 0:
        if (timer) {
            clearTimeout(timer);
            context.set("timer", false);
            node.send({payload:false});
        }
        break;
}
return null;

Where you write the handle to your timeout to a context variable and use clearTimeout to cancel it.

4 Likes

Thanks alot! Both versions work perfectly in theory.
The problem is that the Homebridge node returns the same message around 2 to 6 times in the same second which seems to make the flow fail (always returns "false").
"Join" node doesn´t want to join since the number of messages vary that much.
Is there a way to clean up that mess and get one clean output?
Thank you! :slight_smile:

Try adding a rbe node after the Homebridge node.

[edit] Ok had a look and the issue is in the change node, it needs to reference payload.ContactSensorState, To be sure we would need to see your test flow, as not sure which flow you are using.

Same for my examples @Winni you would need to change the points checking the message payload to check for msg.payload.ContactSensorState

I added that in all of the functions and it seems to work fine for the case of closing the door, although it forwards the "false" message a few times which isn´t a problem. But for the opening it still forwards the whole payload 2x and then returns "false" after the 3 seconds even if i didn´t "close" the door :confused:

[{"id":"2d601bbc.d883bc","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"93672cd.2494bd","type":"hb-event","z":"2d601bbc.d883bc","name":"Haustür","Homebridge":"Homebridge 7954","Manufacturer":"LUMI","Service":"Contact Sensor","device":"Homebridge 79540E:16:A8:A6:79:54LUMIHaustür00000080","conf":"fe2a410c.e36978","x":110,"y":280,"wires":[["ba5b39af.7690a"]]},{"id":"ba5b39af.7690a","type":"switch","z":"2d601bbc.d883bc","name":"","property":"payload.ContactSensorState","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"str"},{"t":"eq","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":310,"y":280,"wires":[["c6175bf0.ff9098"],["c6175bf0.ff9098","a4941f79.81ff78"]]},{"id":"c6175bf0.ff9098","type":"trigger","z":"2d601bbc.d883bc","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"3","extend":false,"units":"s","reset":"0","bytopic":"all","topic":"topic","outputs":1,"x":570,"y":280,"wires":[["a4941f79.81ff78"]]},{"id":"a4941f79.81ff78","type":"change","z":"2d601bbc.d883bc","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"on\": (payload.ContactSensorState =\"1\" ?  true : false) }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":340,"wires":[["e67ac689.6983"]]},{"id":"e67ac689.6983","type":"debug","z":"2d601bbc.d883bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":990,"y":340,"wires":[]},{"id":"fe2a410c.e36978","type":"hb-conf","username":"776-93-574"}]

Do you want the false immediately if a close happens or do you want that also only after the 3 seconds are over and its closed?
Edit:
Than you could try this:

[{"id":"5c34e9c0.c762","type":"inject","z":"c38d0f4.885dff","name":"Open","props":[{"p":"payload.ContactSensorState","v":"1","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":190,"y":360,"wires":[["e92a0fda.792e7"]]},{"id":"c1c9de36.978c98","type":"inject","z":"c38d0f4.885dff","name":"Closed","props":[{"p":"payload.ContactSensorState","v":"0","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":190,"y":440,"wires":[["e92a0fda.792e7"]]},{"id":"e92a0fda.792e7","type":"change","z":"c38d0f4.885dff","name":"Save current","rules":[{"t":"set","p":"Contact1","pt":"flow","to":"payload.ContactSensorState","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":400,"wires":[["5cb8240e.ebe41c"]]},{"id":"5cb8240e.ebe41c","type":"switch","z":"c38d0f4.885dff","name":"Open?","property":"payload.ContactSensorState","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":510,"y":400,"wires":[["36c9d380.37e40c"]]},{"id":"36c9d380.37e40c","type":"trigger","z":"c38d0f4.885dff","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"3","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":660,"y":400,"wires":[["50ae92ea.20e8ac"]]},{"id":"50ae92ea.20e8ac","type":"change","z":"c38d0f4.885dff","name":"Open or Close?","rules":[{"t":"move","p":"payload","pt":"msg","to":"original","tot":"msg"},{"t":"set","p":"payload.on","pt":"msg","to":"($flowContext(\"Contact1\") = 1)? true : false","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":840,"y":400,"wires":[["35542b79.1784b4"]]},{"id":"35542b79.1784b4","type":"debug","z":"c38d0f4.885dff","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1030,"y":400,"wires":[]}]
1 Like

The input from homebridge is a number. My flow was set for string "1"

try

[{"id":"f68be1ad.6c047","type":"switch","z":"8d22ae29.7df6d","name":"","property":"payload.ContactSensorState","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":170,"y":2840,"wires":[["33754e5f.d75b92"],["33754e5f.d75b92","5ce8246.2ac2edc"]]},{"id":"33754e5f.d75b92","type":"trigger","z":"8d22ae29.7df6d","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"3","extend":false,"units":"s","reset":"0","bytopic":"all","topic":"topic","outputs":1,"x":350,"y":2840,"wires":[["5ce8246.2ac2edc"]]},{"id":"5ce8246.2ac2edc","type":"change","z":"8d22ae29.7df6d","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"on\": (payload.ContactSensorState =1 ?  true : false) }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":2900,"wires":[["9f6bcb2f.aacc18"]]},{"id":"9f6bcb2f.aacc18","type":"debug","z":"8d22ae29.7df6d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":2900,"wires":[]}]
1 Like

@JGKK That should be immediatly since i only want to trigger a switch if the door is not closed for 3 seconds but do nothing if its closed.
The flow of @E1cid works now, although it forwards always 3x false, 2x times the original msg Object and after 3 seconds another false, the last message then is the awaited "true" message which i originally wanted to have.

Thank you very very much to you both for your help and effort! :clap:

You can as @E1cid recommended add an rbe node in the start to filter out duplicates.

1 Like

Try a rbe node it will only forward message on change, and will filter out multiple falses or trues
[edit] try after or possibly before the change node also.

It sadly does not help :confused:

Edit:
I tried every possible position in the flow. Does not effect it at all.

When i open:

when i close:

[{"id":"2d601bbc.d883bc","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"93672cd.2494bd","type":"hb-event","z":"2d601bbc.d883bc","name":"Haustür","Homebridge":"Homebridge 7954","Manufacturer":"LUMI","Service":"Contact Sensor","device":"Homebridge 79540E:16:A8:A6:79:54LUMIHaustür00000080","conf":"fe2a410c.e36978","x":110,"y":460,"wires":[["fc1f293.0285dd8"]]},{"id":"f68be1ad.6c047","type":"switch","z":"2d601bbc.d883bc","name":"","property":"payload.ContactSensorState","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":410,"y":460,"wires":[["33754e5f.d75b92"],["33754e5f.d75b92","5ce8246.2ac2edc"]]},{"id":"33754e5f.d75b92","type":"trigger","z":"2d601bbc.d883bc","name":"","op1":"","op2":"","op1type":"nul","op2type":"payl","duration":"3","extend":false,"overrideDelay":false,"units":"s","reset":"0","bytopic":"all","topic":"topic","outputs":1,"x":650,"y":460,"wires":[["5ce8246.2ac2edc"]]},{"id":"5ce8246.2ac2edc","type":"change","z":"2d601bbc.d883bc","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"on\": (payload.ContactSensorState =1 ?  true : false) }","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":520,"wires":[["9f6bcb2f.aacc18","7b76b17d.02fdf"]]},{"id":"9f6bcb2f.aacc18","type":"debug","z":"2d601bbc.d883bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1070,"y":520,"wires":[]},{"id":"7b76b17d.02fdf","type":"hb-control","z":"2d601bbc.d883bc","name":"Die Tür ist auf!","Homebridge":"Homebridge 7954","Manufacturer":"Default-Manufacturer","Service":"Switch","device":"Homebridge 79540E:16:A8:A6:79:54Default-ManufacturerDie Tür ist auf!00000049","conf":"fe2a410c.e36978","x":1110,"y":640,"wires":[]},{"id":"fc1f293.0285dd8","type":"rbe","z":"2d601bbc.d883bc","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload.ContactSensorState","x":250,"y":460,"wires":[["f68be1ad.6c047"]]},{"id":"fe2a410c.e36978","type":"hb-conf","username":"776-93-574"}]

This in a single function node should do exactly what you want:

let timer = context.get("timer") || false;
switch(msg.payload.ContactSensorState){
    case 1:
        if (!timer) {
            timer = setTimeout(()=>{
                context.set("timer", false);
                node.send({payload:{on:true}});
            },3000);
            context.set("timer",timer);
        }
        break;
    case 0:
        if (timer) {
            clearTimeout(timer);
            context.set("timer", false);
            node.send({payload:{on:false}});
        }
        break;
}
return null;
2 Likes

It works but sadly has almost the same behavior as the other flow :frowning:

Can you send me a complete flow of output messages when you open the door and than close it within 3 seconds?
Is the contact when you close it bouncing between on and off?

You you need the false at all ? - or do you just need the true for the door left open ?
If so the you can use @JGKK 's original trigger flow without the change node to set the msg.payload for false.

It sends both on open (true and false) and 3x false when closing

@dceejay
I just need it to set it back to "closed" state either within or after the time offset.
If not the state will stay as "open" after the first opening for longer than 3 seconds.
I trigger a dummy sensor in homebridge to get Homekit sent me a Notification through the home app everytime it´s not closed in a certain time frame.

Can you show the data coming straight from the sensor for that sequence before it goes through the function?

This is the output of both

flow