Automations with timer or delay

For when you do it works great! I have a few timers I was using in places and my own custom restore state automation that handles all the things that I wish had restore capability (timers, trigger template entities, custom device trackers and alerts). After that update I removed timers from the list and used the native restore then fully tested it and found no issues.

Of note it is a little different then your logic (assuming that’s still accurate for your setup). If the timer finished while HA was shut down then it fires the timer.finished event on startup anyway.

As explained by tom_l, that’s not really an automation (it has no trigger). Post the other automation.

I’ve added the trigger that is starting the automation

  - alias: Piscina New Measurement Update
    trigger:
      platform: event
      event_type: ifttt_webhook_received
      event_data:
        action: pool_new_measurement
    condition: []
    action:
      - entity_id: switch.pompa_orp
        service: switch.turn_on
      - data_template:
          message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]"
          title: "Bleach injection: ({{states('input_number.bleach_inject')}} ml)!!!"
        service: notify.SERVIZIO
      - delay: "{{ [0 , 60 * states('input_number.bleach_inject')|float/ states('input_number.bleach_speed')|float ]|max|int }}"
      - entity_id: switch.pompa_orp
        service: switch.turn_off
      - service: input_number.set_value
        data_template:
          entity_id: input_number.bleach_inject
          value: 0
      - data_template:
          message: "{{now().strftime('%H:%M')}}[{{now().day}}/{{now().month}}]"
          title: "Bleach injection end!!!"
        service: notify.SERVIZIO

Your modified automation doesn’t specify a mode so that means it uses the default and runs in single mode. Is that correct or does it actually have mode set to something else like restart or queued?

It’s correct, it runs in single mode

The reason I prefer timers for longer delays is:

  1. If you set the flag it restores the state on reboot,
  2. You can see running timers count down if you want to. I have a lovelace card showing all running timers somewhere.
  3. You can restart timers, cancel them, etc. (You can cancel a delay by disabling the script, but that is awkward)
  4. I think it is simpeler than scripting time logic.
2 Likes

Given that you are a proponent of timers, perhaps you can help rzulian modify the automation to employ a timer.

To recap.
Solution 1
using a timer with restore: true

alias: automation_using_timer
  trigger: your_trigger
  condition: []
  action:
   - entity_id: my_entity
     service: switch.turn_on
   - service: timer.start
     data:
      duration: "{{ 60 * states('input_number.minutes')|int }}"
     target:
      entity_id: timer.my_timer
  initial_state: true
  mode: single

alias: my_timer_finished
  trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.my_timer
  condition: []
  action:
    - service: switch.turn_off
      target:
        entity_id: my_entity
  mode: single

Solution 2
using an automation with a delay and , if needed for critical entities, an automation to manage the HA restart and automation reload.

alias: automation_using_delay
  trigger: your_trigger
  condition: []
  action:
   - entity_id: my_entity
     service: switch.turn_on
   - delay: "{{ 60 * states('input_number.minutes')|int }}"
   - entity_id: my_entity
     service: switch.turn_off
  initial_state: true
  mode: single

alias: automation_for_critical_entities
  trigger:
  - platform: homeassistant
    event: shutdown
    id: HA restart
  - platform: event
    event_type: automation_reloaded
    id: Automation reload
  condition: []
  action:
   - entity_id: my_entity
     service: switch.turn_off
  initial_state: true
  mode: single

Solution 3
similar to solution 1 but setting a input_datetime to store when the second automation have to trigger.
I’m not sure what happens if a restart or reload occurs during the my_stop_time. Does the trigger run after the restart?

alias: automation_using_input_datetime
  trigger: your_trigger
  condition: []
  action:
   - entity_id: my_entity
     service: switch.turn_on
   - service: input_datetime.set_datetime
     data:
       datetime: "{{ now() + timedelta(minutes= states('input_number.minutes')) }}" 
     target:
       entity_id: input_datetime.my_stop_time 
  initial_state: true
  mode: single

alias: my_stop_time
  trigger:
    platform: time
    at: input_datetime.my_stop_time
  condition: []
  action:
    - service: switch.turn_off
      target:
        entity_id: my_entity
  mode: single
1 Like

Solution 1 - This looks good. Should work and be restart/reload proof

Solution 2 - I mean it works but your switch may shut off early. If you restart HA it’ll immediately shut off your switch. So if input_number.minutes is set to 120 and it turned off 30 seconds after you turned it on because you happened to restart HA I’d consider that a bug.

Solution 3 - This is a slightly less resilient version of the timer. For the most part it works just as well unless HA happens to be restarting or automations reloading at exactly the time set to input_datetime.my_stop_time. Which admittedly is pretty unlikely, its not a huge flaw. But there’s really no advantage to it over Solution 1: both require two automations, both require an extra helper. The only difference is Solution 1 is completely restart/reload proof whereas this one is 99% restart/reload proof. Therefore I don’t know why someone would pick this over Solution 1.

Another option for the turn off automation is to use a time pattern trigger (/5, /10, /15 minutes as desired), and then a state condition (not a trigger) to check and see if the pump has been on for more than 30(or whatever) minutes.

It removes the need for any helpers. It won’t be as precise, and may not fit if you want xx minutes and not approximately xx minutes.

The pump may run for a full xx minutes after a restart, but will shut off and not run all day.

It may not be appropriate for this use case if there are non-automated times the pump should be running more than xx minutes at a time.

The following single automation:

  1. Turns on the switch upon receiving a specific event
  2. Turns off the switch at the time specified by input_datetime.pompa_orp
  3. The switch’s on duration is controlled by input_number.pompa_orp
  4. After a restart it sets the switch to the appropriate state
alias: Piscina New Measurement Update
trigger:
- platform: event
  event_type: ifttt_webhook_received
  event_data:
    action: pool_new_measurement
- platform: time
  at: input_datetime.pompa_orp
- platform: homeassistant
  event: start
condition: []
action:
- variables:
    switch: switch.pompa_orp
- choose:
  - conditions: "{{ trigger.platform in ['event', 'time'] }}"
    sequence:
    - service: "switch.turn_{{ iif(trigger.platform == 'event', 'on', 'off') }}"
      target:
        entity_id: '{{ switch }}'
    - service: input_datetime.set_datetime
      target:
        entity_id: input_datetime.pompa_orp
      data:
        timestamp: "{{ (now() + timedelta(minutes = states('input_number.pompa_orp')|int(0) * iif(trigger.platform == 'event', 1, -1))).timestamp() }}"
  - conditions: "{{ trigger.platform == 'homeassistant' }}"
    sequence:
    - service: "switch.turn_{{ 'on' if now() < states('input_datetime.pompa_orp') | as_datetime else 'off' }}"
      target:
        entity_id: '{{ switch }}'
  default: []

It’s based on the same principle I have used, for a over a year, for all of my scheduled automations to ensure they aren’t affected by restarts. A Time Trigger is controlled by an input_datetime that is dynamically set to a time in the future representing when the device should be turned off. The automation is also triggered by a restart and then determines if the device should be turned on or off.


EDIT

Correct typos.

1 Like

The problem with this approach is that, as far as I can tell, you cannot stop this automation from interfering with the switch when you do not want it to. At startup, it either turns the switch on or off, regardless if an event has happened to warrant switch operation. If you also want manual operation, you need to manipulate the input helper to prevent it from undoing the manual switch.

Besides that, it is hard to read. You could compensate for the downsides, but that makes it even more complex.

A timer is simple, and when it isn’t running, it doesn’t interfere. That keeps automations clean, especially if there are multiple automations operating the same switch.

Your comment suggests you may not have understood its operating principle.

It’s a choose with two choices. The first choice controls the switch under normal conditions and the second one only when Home Assistant restarts.

The most complex part of it is the arithmetic it performs to set the input_datetime’s value (and it’s not all that complicated).

What did I not understand? You have a trigger at startup. That executes a choose that either turns the switch on or off. Even if that is a week after the time in your input helper, it will still perform an action. I think that it is not ok for a home assistant reboot to always operate switches. So maybe you do not understand me?

A timer will only execute if it was started before the reboot. This will always act, regardless what happened before the reboot. It needlessly repeats an action that has already happened in the past, and is maybe undoing something else alltogether, that has nothing to do with the automation at hand. It is an unwanted side effect i.m.o.

You wrote it, you understand, but I apparently misunderstood something. The fact that you need to explain to me it is right proves that it is complex.

I didn’t realize your experience level was the de facto measuring stick for complexity. :man_shrugging:

As for your commentary regarding perceived deficiencies, I haven’t encountered any in a year’s worth of real-world operations. I prefer my automations to be, on startup, very deterministic and fail-safe so, yes, I want scheduled critical devices to be explicitly set to on or off. Given that this application involves a bleach pump, I assumed rzulian would want the same behavior.

I encourage you, again, to apply your knowledge of timers and compose an automation for rzulian that employs it.

I’ve been a professional software developer for over 20 years, so it is not that I do not understand what it does. I know that if something is hard to read, it is prone to errors.

This is your preference and you are entitled to it. I expressed my preference, and explained why.

It is up to the person copying your code to decide to use it or not. But for those who do are not fluent in jinja2 I hope it is now clear that if you manually toggle the switch and reboot, HA will toggle it back.

Excellent, so you should have no difficulty supplying rzulian with a functional example, employing a timer, to control the bleach pump.

The answers to the questions are there, above your post.

Effectively this is solution 3 as proposed above. You can consolidate solution 1 into one automation in the same way like so:

alias: Piscina New Measurement Update
trigger:
- id: pool_new_measurement
  platform: event
  event_type: ifttt_webhook_received
  event_data:
    action: pool_new_measurement
- id: timer_finished
  platform: event
  event_type: timer.finished
  event_data:
    entity_id: timer.pompa_orp
condition: []
action:
  if: "{{ trigger.id == 'pool_new_measurement' }}"
  then:
    - service: switch.turn_on
      target:
        entity_id: switch.pompa_orp
    - service: timer.start
      data:
        entity_id: timer.pompa_orp
        duration: "{{ states('input_number.pompa_orp') | int(0) * 60 }}"
  else:
    service: switch.turn_off
    target:
      entity_id: switch.pompa_orp   

Since the trigger platform is the same for both triggers now I used trigger IDs to separate but same idea.

This should be as resilient as what you are showing but using a timer instead. Because the way the new timers with restore work is if HA is restarting when the timer is supposed to finish then the timer.finished event fires on startup. So they both are able to handle HA restarts at any time without issue.

Although come to think of it neither can deal with an automation reload at exactly the wrong time. You might want to consider adding a trigger for the automation_reloaded event to cover that. The timer approach unfortunately doesn’t have a good way to handle an automation reload exactly when the timer is finishing. I didn’t think that through initially, if an automation reload causes this to miss the timer.finished trigger then it won’t get another chance.

It’s incomplete, contains syntax errors, some pseudo-code, and is needlessly split into two automations. Anyway, CentralCommand has already stepped up with a good example.

Good point. I haven’t found the need for it (yet) in my own automations because the duration of Reload Automations is so brief (relative to a restart). Nevertheless, you’re right; for full protection it should trigger on that event as well.

I think a timer might also be susceptible to a Reload Timers that occurs precisely at a timer event (like finished). It’s a narrow window of opportunity for failure but, just like for automation_reloaded, there’s no obvious way to mitigate it for timers.


Dear Reader,

Just to clarify, I am not opposed to employing a timer. The fact is that I used timers extensively in Premise (home automation software I used for over a decade prior to Home Assistant). However, historically, Home Assistant’s timer could not survive a restart (unless you implemented a workaround like this one).

The ability to restore a previously active/paused timer on restart was introduced as a native feature very recently (April 2022) Before that, a restart would cancel an active/paused timer thereby making it unsuitable for time-critical applications. Necessity is the mother of invention and so the job was handled by an input_datetime (and a little bit of date arithmetic).

Now that a timer can be configured to survive a restart, it becomes a viable means of handling time-critical applications. The caveat is that it still has one or two chinks in its armor (reload events) that have the potential to negatively impact it. In fairness, for most applications the odds are small but one should be aware they exist when designing a failure-resistant automation.

FWIW, most of my time-critical applications use the input_datetime technique but a few use a timer (wherever I need to see a countdown in the UI) with the workaround mentioned above (I have yet to convert to using the new native method of restoring timers). It’s good to have choices.