How to replicate "for" in a condition

I have a simple automation that – upon a printer reaching it’s conclusion – turns off the power. The reason for this is the printer maintains 13 watts in “standby” mode. While printing, the printer may occasionally use a low amount of power, but I know that if it stays below 20 watts for 5 minutes it is definitely in standby mode, thus can be safely powered off.

The below automation accomplishes that.

The problem is there seem to be a variety of instances (related to restarting home assistant) whereby the 3D printer is on, the power is stable at 13 watts, and the trigger is never triggered because the state never changes. For example, I have the “restart mode” of the Sonoff S31 set to always on, so if something happens it does not shut off the power. So if home assistant restarts, the device is on. I’d like it to stay on for 5 minutes then shut off.

So, I thought about adding a second trigger and checking every hour or so to make sure it catches these scenarios. Unfortunately, I cannot put my “numeric state below 20 for 5 minutes” as a condition, at the numeric_state condition does not support “for”

Any ideas on how to replicate the “for” of a numeric_state condition?
Or other creative ideas to rework the automation to turn off after 5 minutes of < 20 watts in all circumstances, even after a recent restart?

Thanks!

alias: Turn off printer when finished
description: 
trigger:
  - platform: numeric_state
    entity_id: sensor.s31_power
    below: 20
    for:
      hours: 0
      minutes: 5
      seconds: 0
condition: null
action:
  - service: switch.turn_off
    data: {}
    target:
      entity_id: switch.s31_relay
mode: single

Have you tried @Sbyx’s “Appliance Has Finished” blueprint?

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Thank you for the suggestion! I have not, and I will.

I am reviewing the code for that blueprint now. It appears to be fundamentally the same as what I did – with a trigger and no condition. Won’t this suffer from the same problem – IE if Home Assistant is booted into an environment where the power is already below the threshold, but also not changing, that it will not trigger? It seems the trigger requires the numeric state to change, or cross over the threshold, or something – to trigger. Deceptively – it’s not as simple as if the value is below XX for YY minutes, it will trigger.

I want to make sure I understand the various principles of the automation so I can make my own, more complex, automations in the future. After searching around I haven’t found much discussion of this topic.

EDIT: Tested the blueprint and can confirm that it does NOT perform the action (turn off switch) even if the sensor is below the threshold for the requisite time – at least, not when entered immediately into an environment where the item is already below the threshold and the value is not changing. Basically, nothing is changing to make the trigger happen.

You have a couple options. The first is to set up a timer that it started and cancelled based on the power sensor.

alias: Fläkt 30min
description: ""
trigger:
  - platform: numeric_state
    entity_id: sensor.s31_power
    below: 20
    id: start
  - platform: numeric_state
    entity_id: sensor.s31_power
    above: 20
    for: "00:01:00"
    id: cancel
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.printer_idle
condition: []
action:
  - if: "{{ trigger platform == 'numeric_state' }}"
    then:
      - service: timer.{{trigger.id}}
        target:
          entity_id: timer.printer_idle
    else:
      - service: switch.turn_off
        target:
          entity_id: switch.s31_relay
mode: single

Another option, if the state is stable… Since the last_changed property updates on restart, you could add a template trigger to execute the “turn off” in those cases.

  - platform: template
    value_template: |
      {% set pr_sensor = 'sensor.s31_power' %}
      {{ states(pr_sensor) | int < 20 and
      now() - states[pr_sensor].last_changed > timedelta(minutes=5) }}

Another option you can do straight from the UI, is use a threshold sensor helper. It creates a binary sensor based on a value crossing certain limits. Using that you could create a sensor “Printer active” (or idle, what you wish). Because it is a binary sensor, you can use for in conditions.

In fact, I wasn’t aware numeric states did not support for because I always prefer thresholds so I never noticed it solved this problem until I read this. I prefer thresholds because they also use a hysteresis. You can for instance say: it will turn on when it exceeds 20, but won’t go off again until the value is below 14. You can do this by setting the limit to 17 an the hysteresis to 3. It helps reduce flip-flop behaviour when the value fluctuates around the set bound.

The docs are for a yaml config, but you can create a threshold in the helper section. It will then show up with the device it monitors:

While it would allow the use of a duration in the condition block, switching to a threshold sensor wouldn’t solve the restart issue. OP would still need an uptime-based trigger to initiate checking the condition… and, depending on timing, using the threshold sensor with a timer could cause the timer-based trigger to be blocked from executing the actions.

Thanks all for the interesting and informative discussion. I will be re-reading to make sure I understand and testing these different approaches soon.

What an interesting issue!

I don’t understand how that would happen. The time trigger activates, the condition on the threshold evaluates, the action is either triggered or not. Nothing is waiting, nothing is blocking.

Yes, restart will still require a fallback. That could also be a triggerevaluate some time after a restart. No timed actions needed.

Unfortunately, judging from the code, this does not act after restarts. It actively waits inside the automation too, and has single execution. I think this blueprint is even more prone to edge cases where it will not always do as needed.