Wait until "FOR" but in the middle of the Node RED flow

I’m trying to achieve this logic:

  1. When the motion sensor off -> on, turn on the light(if the light off)
  2. Wait for either:
    1. Motion sensor off -> on FOR 3min, then turn off the light
    2. Light is off -> on manually, do nothing

The logic can be perfectly implemented using HA automation:

##========== Auto kitchen light ==========##
- id: auto_kitchen_light
  alias: "Auto kitchen light"
  trigger:
    platform: state
    entity_id: binary_sensor.kitchen_motion_occupancy
    from: "off"
    to: "on"
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: light.kitchen_light
        state: "off"
  action:
    - service: light.turn_on
      target:
        entity_id: light.kitchen_light
    # Wait for the motion to stop for 3 minute
    # or the light is manually turned off
    - alias: "wait"
      wait_for_trigger:
        - platform: state
          entity_id: light.kitchen_light
          from: "on"
          to: "off"
        - platform: state
          entity_id: binary_sensor.kitchen_motion_occupancy
          from: "on"
          to: "off"
          for:
            minutes: 3
    # Stop execution if the light is turned off manually
    - condition: template
      value_template: "{{ wait.trigger.idx == '1' }}"
    - service: light.turn_off
      target:
        entity_id: light.kitchen_light

I’m trying to move some automations to Node RED, and the wait until FOR logic is causing me headache. So far I have implemented every other logic like this, but cannot find an elegant way to “wait till motion OFF -> ON for 3 min

Note that the cross between two wait_for is to cancel the other wait to avoid Node RED using too much resources. A little overkill.

I found a similar post Wait until FOR? but it uses wait_until as the trigger of the flow, whereas I’m using it in the middle of the flow, so the solution doesn’t really apply.

I guess an ugly workaround is to set the wait untill timeout to, e.g., 5s, and add a loop feedback from its output to inputs, but that’s not very efficient.

Any ideas? Thanks!

I did not look at your code to troubleshoot but there is a good discussion about Motion activated light automation here:

Specifically, you might want to try this Node-Red flow:

[{"id":"acba2a367ae2800b","type":"trigger-state","z":"120358abd7c22d30","name":"","server":"","version":4,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entityId":"binary_sensor.kitchen_motion_occupancy","entityIdType":"exact","debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"on"},{"targetType":"this_entity","targetValue":"","propertyType":"previous_state","propertyValue":"old_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"off"},{"targetType":"entity_id","targetValue":"light.kitchen_light","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"off"}],"customOutputs":[],"outputInitially":false,"stateType":"str","enableInput":false,"x":360,"y":6496,"wires":[["d61c16a7c8be5162","bbd989de489bbfca"],[]]},{"id":"d61c16a7c8be5162","type":"api-call-service","z":"120358abd7c22d30","name":"","server":"","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.kitchen_light"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":678,"y":6496,"wires":[[]]},{"id":"bbd989de489bbfca","type":"ha-wait-until","z":"120358abd7c22d30","name":"","server":"","version":2,"outputs":1,"entityId":"binary_sensor.kitchen_motion_occupancy","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"0","timeoutType":"num","timeoutUnits":"seconds","checkCurrentState":true,"blockInputOverrides":true,"outputProperties":[],"entityLocation":"data","entityLocationType":"none","x":668,"y":6544,"wires":[["0dcaf05602dfc7d0"]]},{"id":"0dcaf05602dfc7d0","type":"delay","z":"120358abd7c22d30","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":820,"y":6544,"wires":[["df33b2be335f3ea5"]]},{"id":"df33b2be335f3ea5","type":"api-call-service","z":"120358abd7c22d30","name":"","server":"","version":5,"debugenabled":false,"domain":"light","service":"turn_off","areaId":[],"deviceId":[],"entityId":["light.kitchen_light"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":982,"y":6544,"wires":[[]]},{"id":"d9f3e52788f6319d","type":"server-state-changed","z":"120358abd7c22d30","name":"reset timers","server":"","version":5,"outputs":2,"exposeAsEntityConfig":"","entityId":"light.kitchen_light_basic","entityIdType":"exact","outputInitially":false,"stateType":"str","ifState":"off","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"reset","propertyType":"msg","value":"true","valueType":"bool"}],"x":486,"y":6592,"wires":[["bbd989de489bbfca","0dcaf05602dfc7d0"],[]]}]

edit:

There is an issue with this that is the motion sensor changes state to off and within that 3-minute window the state changes back to on the light will still turn off after 3 minutes. An event needs to be watched for the motion sensor turning on and then resetting the delay node.

I actually have this exact logic working with a motion sensor but without using a timer in Node-Red. I can do this because the motion sensor has a built-in wait time (sometimes referred to as the “blind time” or “dwell” time). So, I have my blind time set to x minutes which will send a “no motion” event (off) after there is no motion for x minutes.

The benefit is flow simplicity. But timer adjustments must be done in the sensor itself - not in Node-Red.

image

[{"id":"97e6a3ea.2d8cd","type":"api-call-service","z":"fe1a8042.af255","name":"Garage Light On","server":"","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.garagelightswitch"],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":310,"y":191,"wires":[["4e0859a3.102238"]]},{"id":"4ff57502.55030c","type":"api-call-service","z":"fe1a8042.af255","name":"Garage Light Off","server":"","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.garagelightswitch"],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":519,"y":248,"wires":[[]]},{"id":"a1f8653c.15a248","type":"trigger-state","z":"fe1a8042.af255","name":"Garage Motion","server":"","version":4,"inputs":0,"outputs":2,"exposeAsEntityConfig":"","entityId":"binary_sensor.garage_motion_motion","entityIdType":"exact","debugEnabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"on"},{"targetType":"entity_id","targetValue":"switch.garagelightswitch","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"off"}],"customOutputs":[],"outputInitially":false,"stateType":"str","enableInput":false,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"x":102,"y":191,"wires":[["97e6a3ea.2d8cd"],[]]},{"id":"4e0859a3.102238","type":"ha-wait-until","z":"fe1a8042.af255","name":"No Motion","server":"","version":2,"outputs":1,"entityId":"binary_sensor.garage_motion_motion","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"0","timeoutType":"num","timeoutUnits":"minutes","checkCurrentState":true,"blockInputOverrides":true,"outputProperties":[],"x":290,"y":249,"wires":[["4ff57502.55030c"]]}]

I’m quite partial to using stoptimer-varidelay. Here’s a simple flow that does what you’re asking for (adapted from my own existing flow). Bonus is that you can do some other cool stuff with the node.

Note the “stop” issued via msg.payload on Turn on Arthur Dimmer. This stops the timer after motion is turned on.

[{"id":"d45c52dbba6a2206","type":"server-state-changed","z":"234ae773a4e54a3e","name":"Arthur Bedroom motion","server":"","version":5,"outputs":2,"exposeAsEntityConfig":"","entityId":"binary_sensor.arthur_zse40_motion_detection","entityIdType":"exact","outputInitially":false,"stateType":"str","ifState":"on","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":740,"y":360,"wires":[["15604de8a96ca6ac"],["b5a3fc349511491e"]]},{"id":"b5a3fc349511491e","type":"api-current-state","z":"234ae773a4e54a3e","name":"arthur light on?","server":"","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"light.arthur_dimmer_level","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":1000,"y":360,"wires":[["239078a23062757f"],[]]},{"id":"239078a23062757f","type":"stoptimer-varidelay","z":"234ae773a4e54a3e","duration":"10","durationType":"num","units":"Minute","payloadtype":"num","payloadval":"0","name":"Arthur Light Timeout","reporting":"last_minute_seconds","persist":true,"ignoretimerpass":true,"x":1320,"y":360,"wires":[["2c56dfedd42da918"],[],[]]},{"id":"2c56dfedd42da918","type":"api-call-service","z":"234ae773a4e54a3e","name":"Turn off Arthur Dimmer","server":"","version":5,"debugenabled":false,"domain":"light","service":"turn_off","areaId":[],"deviceId":[],"entityId":["light.arthur_dimmer_level"],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1580,"y":360,"wires":[[]]},{"id":"15604de8a96ca6ac","type":"api-call-service","z":"234ae773a4e54a3e","name":"Turn on Arthur Dimmer (70%)","server":"","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.arthur_dimmer_level"],"data":"{    \"brightness_pct\": 70 }","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"stop","valueType":"str"}],"queue":"none","x":1040,"y":280,"wires":[["239078a23062757f"]]}]