Wait until node in nodered doesn't work anymore as expected

To say that a node does not work as expected, without providing the context of the flow, message data being passed, and the entire story of what you want to achieve makes it impossible to debug or provide help. Now that we have some actual flows, these can be replicated and investigate properly - thank you!

As I understand, the flow is intended to identify a list of entities requiring attention (blinds need closing), to pass each entity from the list singularly to an Action node (close the blind), use the Wait Until node to wait until the blind responds as closed, then loop back to pick the next entity from the list. Repeat until done.

As I understand, the simplistic test-bed flow takes a list of things (switches will do) and loops through them, waiting for each entity (switch) to change property, before triggering then looping back to the next entity in the list.

The conjecture is that the Wait Until node is not working.

I have set up a similar flow to your test, in a machine with the latest updates, and I can confirm that it all works for me, as I would expect, with each entity change causing the Wait Until node to trigger in turn, as expected.

My conjecture is that the way you are attempting to use the node is the problem.

Here is my test flow.

I call a list of two smart plugs (switches), the flow picks the first from the list, sets the Wait Until node to wait until the switch state object is updated, then loops round for the next entity, and repeats until the list is empty.

Here is the flow, in case you want to try this for yourself.

[{"id":"5a6e9d7abd7ada4e","type":"inject","z":"3d7cf3b151968f34","name":"Device List","props":[{"p":"devices","v":"switch.t2_2,switch.t3_2","vt":"str"},{"p":"list","v":"$split(devices,\",\")","vt":"jsonata"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":120,"y":3080,"wires":[["473e9b7e43625363"]]},{"id":"0d800e638c79ca63","type":"switch","z":"3d7cf3b151968f34","name":"Until list is empty","property":"payload","propertyType":"msg","rules":[{"t":"nempty"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":510,"y":3080,"wires":[["6dc2575d4549d0db"],["ec78cd0c67170ccd"]]},{"id":"473e9b7e43625363","type":"change","z":"3d7cf3b151968f34","name":"Pick entity","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t   \"entities\": list[0]\t}","tot":"jsonata"},{"t":"set","p":"list","pt":"msg","to":"list[[1..$count($$.list)]]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":3080,"wires":[["0d800e638c79ca63","ef352fe25636be99"]]},{"id":"709de0a68a6913dc","type":"link in","z":"3d7cf3b151968f34","name":"NEXT","links":["e290ca8a62b12c75"],"x":130,"y":3120,"wires":[["473e9b7e43625363"]],"l":true},{"id":"e290ca8a62b12c75","type":"link out","z":"3d7cf3b151968f34","name":"NEXT","mode":"link","links":["709de0a68a6913dc"],"x":1030,"y":3060,"wires":[],"l":true},{"id":"54f0fa6fe33fd57f","type":"server-state-changed","z":"3d7cf3b151968f34","name":"","server":"","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["switch.t2_2","switch.t3_2"],"substring":[],"regex":[]},"outputInitially":true,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":290,"y":2980,"wires":[["26a49bba6d54dc2b"]]},{"id":"26a49bba6d54dc2b","type":"debug","z":"3d7cf3b151968f34","name":"Watch state change","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"data.entity_id & \" has gone \" & payload","targetType":"jsonata","statusVal":"data.entity_id & \" has gone \" & payload","statusType":"auto","x":590,"y":2980,"wires":[]},{"id":"6dc2575d4549d0db","type":"ha-wait-until","z":"3d7cf3b151968f34","name":"Wait in turn","server":"","version":3,"outputs":2,"entities":{"entity":[],"substring":[],"regex":[]},"property":"state","comparator":"jsonata","value":"$millis()-$toMillis($entity().last_updated)<7000","valueType":"jsonata","timeout":"30","timeoutType":"num","timeoutUnits":"seconds","checkCurrentState":false,"blockInputOverrides":false,"outputProperties":[],"x":710,"y":3080,"wires":[["c4e611a5b7f454ec","f247ea0dbf7d6e1a"],["25b64679aa79ae07"]]},{"id":"c4e611a5b7f454ec","type":"debug","z":"3d7cf3b151968f34","name":"Responded","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":910,"y":3000,"wires":[]},{"id":"25b64679aa79ae07","type":"debug","z":"3d7cf3b151968f34","name":"Timed out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":900,"y":3120,"wires":[]},{"id":"ef352fe25636be99","type":"debug","z":"3d7cf3b151968f34","name":"each entity","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload.entities","targetType":"msg","statusVal":"payload","statusType":"auto","x":290,"y":3120,"wires":[]},{"id":"f247ea0dbf7d6e1a","type":"delay","z":"3d7cf3b151968f34","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":900,"y":3060,"wires":[["e290ca8a62b12c75"]]},{"id":"ec78cd0c67170ccd","type":"debug","z":"3d7cf3b151968f34","name":"stop","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"\"All Done\"","targetType":"jsonata","statusVal":"\"All Done\"","statusType":"auto","x":530,"y":3120,"wires":[]}]

So why can I make it work and your flow appears to fail? Three reasons I think.

First, the use of templates. It is a hard fact that whilst Home Assistant runs on templates, Node-RED does not support them. There are a few nodes where it is documented that templates can be used, however the nodes and UI fields are very limited and correct functioning is not assured. I would strongly advise anyone using Node-RED to avoid templates and use JSONata for data management and the documented input-overrides provided for nodes that permit this method of dynamically setting the UI fields.

The Wait Until node accepts msg.payload as an object with “entities” field for a singular entity id in place of the UI entity field. I have used a bit of JSONata to capture the string list into an array, then at each loop
list[0] picks of the first item in the list, and constructs the correct input object for msg.payload to set the entity id. The remaining part of the list can be extracted simply by list[[1..$count($$.list)]] which may look incomprehensible but simply takes the array from index 1 to the end, thus doing the work of your function node.

This now removes the need for using {{device}} to set the entity.

Second, the Wait Until state property is incorrect.
I don’t like using this node, simply because every time I do it freezes my webpage. The property UI box does a global search for every single property in every single entity. For the ‘state’ property this is a) easy to type, quickly before my screen freezes, and b) guaranteed as every entity has a state.

You are using data.new_state.last_updated which is I believe only related to the Event: state node. The Event: state node (triggers from a state/attriibute change) will return an event object in the msg.data field with both old_state and new_state objects, being the pre and post entity object and each include state and last_changed/updated fields. When using the output from the Event: state node, we have to specifiy which object we want.
The Wait Until node outputs just the entity object. You can check this by setting an output property and you will see only the ‘entity’ option, which returns the singular post-trigger entity object.

I believe that the correct property to use in your case would be last_updated. This, of course, would explain why the node is not responding - it is looking at the property change of a completely different node.

Third, the conditional test look to me as though it should never work. The last_updated entity field has an ISO UTC timestamp string, hence 2024-10-11T13:04:17.831329+00:00 which cannot be compared to “0”. It can’t actually be compared to very much of real use.

My presumption is that you wish for the Wait Until node to trigger when the entity in question updates for any reason. A test for ‘state’ as ‘on’ or ‘off’ would work, hence state in [“on”, “off”] or something similar.

A direct conditional test against the last_updated field would be difficult, hence I have changed the conditional test to use a JSONata predicate. Rather than test

‘property’ ‘is’ ‘value’

the JSONata expression option allows for entering an expression which needs to evaluate as either true or false. When the expression is evaluated and returns true, the node will fire, treating this as the ‘until’ in the Wait Until action.

I have used

$millis()-$toMillis($entity().last_updated)<7000

This get the Unix millisecond timestamp integer for now, and subtracts the millisecond equivalent of the node’s entity, last _updated field. If this is less than seven seconds, the Wait node will trigger.

When the entity object under consideration (the entity-id of the node) changes, the last_updated field will be set, and the node will run the ‘until’ condition test, thus evaluating the JSONata expression and testing for true. I have allowed 7 seconds here since my switches, when manually operated, can take up to 4 or 5 seconds before the change is seen.

This test works, for any change hence the blind could be closed, open, or just updated, hence it may not be quite what you want, however it is working for me.

And finally. Since the Wait Until node is just a message gatekeeper, when the ‘until’ condition is met, the node simply passes on the orginal message it first received. As is, and without any change. Yes you can add output properties if you so wish, but these are only set on successful trigger, and you should use the $entity() JSONata function which operates on the post-event entity object as is.

I hope this has been of some use.

1 Like