Understanding automations with time delays and how to control them

I’m hoping someone has some advice for me here. I’m new to HA but a long time Homeseer user, in the process of migrating to HA. I’ve got a simple scenario here and I’m having trouble finding the right solution.

Desired action: Pressing a button triggers an automation that turns on a light for exactly ten minutes, then turns it off. Pressing the button again within the 10 minute window restarts the 10 minute timer.

In Homeseer, each time an “event” is triggered with some delayed action (in this case, turning the light off after 10 minutes) a new temporary event is created to turn off the light at a specific time. This temporary event can be easily deleted if it exists and the button press event runs again, so it effectively does what I need it to do.

In HA, I’ve been reading about two methods:

  1. service.automation_off then service.automation_on with the stop_actions set to True. This seems to work but I have to call it in a separate automation – I am assuming calling those services within an automation will stop it completely, which is not what I want to do.

  2. There’s some talk about having the automation call a script, and then controlling that with some call to cancel/stop a running script. If I was going to do that I’d rather just make the two automations since that seems easier to me. One automation has the button trigger. The other has a ten minute light on action. The first stops/starts the second, then calls the second for the light sequence.

I was hoping there’s some other way I am not familiar with yet.

Finally, one thing I’ve noticed that I guess is intended behavior but it doesn’t seem right to me. If I trigger the automation to turn on the light for 10 minutes, and a few minutes into it I trigger the same automation, the light will still turn off 10 minutes from the first trigger, not 10 minutes from the second trigger. In other words, triggering an automation again that’s already running with a delay doesn’t seem to have any effect.

1 Like

You just set the mode of the automation to restart instead of single. Then if it is running and you press the button again, it simply restarts the automation, which restarts the timer.

5 Likes

What mobile.andrew.jones said.

Something else to be aware of: if your automation employs a 10 minute delay to turn off the light, it will be reset if you Reload Automations or restart Home Assistant during its countdown.

This behavior isn’t limited to delay. Basically anything that waits, or iterates like repeat, is cancelled by a reload/restart. Timers are unaffected by a Reload Automations; they’re affected by Reload Timers and restart Home Assistant.

If you use the Automation Editor, it performs a Reload Automations whenever you save a new or modified automation.

Currently, Home Assistant has no means tof incrementally reloading automations/scripts/timers (i.e. only the new/modified ones) and simply reloads all of them (thereby cancelling anything in progress).

FWIW, there are techniques to mitigate it but they all make the automation a bit more complex. I employ them wherever I feel the length of time spent within the automation’saction makes it vulnerable to a reload/restart.

For example, instead of using a delay to turn off the light, when triggered by the button, set an input_datetime to a time ten minutes in the future. Use a Time Trigger in the automation that employs the input_datetime. The automation’s action employs a choose to determine if it was triggered by the Time Trigger and turns off the light. I use this technique and it can survive a reload/restart.

Thanks for the great info, this is very helpful. The “single” vs. “restart” was the clue to getting my automation to work properly. A couple things I noticed:

  1. Originally I had one automation that detected a button press, and then used the “choose” feature of an action and checked the “device” - the light - to see if it was off, and if so run the timed cycle.

  2. The other choice in the action was if the light (device) was on, then turn it off.

The problem with the above was that if I pressed the button to turn the light on, then a minute later pressed the button to turn the light off, that worked. But pressing the button a third time to restart the timer caused the light to stay on indefinitely.

I recalled that if you want to see if something is “on or off” you check state not device. When I changed the condition to check state (on or off) the whole thing works properly. Not sure why that would make a difference but could someone confirm that if you want to have a condition based on the state of a device that you should check state and not the device itself?

Finally, I thought this was cool for the delay:

- delay: '{{ range(10,30)|random }}' gives a random 10-30 second delay.

If you’re interested, post your automations and we can review them and make suggestions.

Yes, I will actually because what I thought was the issue wasn’t. The light stayed on and never went off after the 3rd button press from my above scenario. One thing to ask though – is there a timeout for automations? I swear this all worked when I set my timer to 1 minute, but then when I made it the full 30 minutes the script failed (it never turned the device off after 30 mins.)

Note, I simplified the scenario when originally posting. The actual script is to run an air filter in a bedroom for 30 minutes. This is done manually when the “cat thinks outside of the box” if you know what I mean :slight_smile:

Anyway, here’s the script and I’m open to thoughts:

alias: 'Master Bedroom: Air filter 30 minutes'
description: ''
trigger:
  - device_id: b251a3bee8687b95b2fff6d
    domain: deconz
    platform: device
    type: remote_button_short_press
    subtype: turn_on
condition: []
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: switch.mbr_air_filter_on_off
            state: 'off'
        sequence:
          - type: turn_on
            device_id: d8117da0fbe2652aa5d5c
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
          - delay:
              hours: 0
              minutes: 30
              seconds: 0
              milliseconds: 0
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
      - conditions:
          - condition: state
            entity_id: switch.mbr_air_filter_on_off
            state: 'on'
        sequence:
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
    default: []
mode: restart

Create a counter, increment on press, use switch case to set light modes, on last press in cycle, reset counter and programmatic call switch press event to start cycle again. If you want timed lights start a timer, separate automation for timer end to shut off lights and reset counter. The 2 automation setup with timer works great with motion sensors, you can even change timer duration in the automation and for different time lengths based on logic. Have multiple on sensors and automation call same timer with 1 off automation, etc…

I’m wondering if I am running into this issue and that’s why my 30 minute timers seem to fail. I might have been working on another automation while waiting for the 30 minute test to complete. If the automations were reloaded when I finished working on another one, I assume any that were pending a delay were stopped.

my 5 cents bro

I dont like using delay I think the logic get lost

I use timer

some happen turn timer ON

The last action in a automation

  action:
    .......
    - service: timer.start
      data:
        duration: '00:10:00'
      target:
        entity_id: timer.hangout

we can create a automation when finish

- id: 8e10272d-5447-433f-b7f8-f267eaa66fee
  alias: "Timerstop"
  trigger:
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.hangout
  action:
# do the Finished logic

we can check the state of it
in a condition

some happen BUT is the timer running

  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: timer.hangout
        state: active

do a action

ie
washing get done timer starts and wife not home alexa tell me to hang it every 10 mins
when timer finish it start again ( one automation) until I open the washing door then time cancel (other automation)

Create an input_datetime that stores date and time. In the following example, I named it:

input_datetime.mbr_air_filter_on_off

Here’s the suggested automation. Its 30-minute time period can survive a restart because it employs a Time Trigger instead of a delay.

alias: 'Master Bedroom: Air filter 30 minutes'
description: ''
trigger:
  - device_id: b251a3bee8687b95b2fff6d
    domain: deconz
    platform: device
    type: remote_button_short_press
    subtype: turn_on
  - platform: time
    at: input_datetime.mbr_air_filter_on_off
condition: []
action:
  - variables:
      switch: switch.mbr_air_filter_on_off
      is_on: "{{ is_state(switch, 'on' }}"
  - choose:
      - conditions: '{{ trigger.platform == 'device' and not is_on }}'
        sequence:
          - service: switch.turn_on
            target:
              entity_id: '{{ switch }}'
          - service: input_datetime.set_datetime
            target:
              entity_id: input_datetime.mbr_air_filter_on_off
            data:
              timestamp: "{{ (now() + timedelta(minutes=30).timestamp()) | int(0) }}"
      - conditions: '{{ trigger.platform in ['device', time'] and is_on }}'
        sequence:
          - service: switch.turn_off
            target:
              entity_id: '{{ switch }}'
    default: []
mode: single
1 Like

OK, thank you for the date/time option script. I don’t fully understand it but I’m willing to give it a try. In the meantime, I am playing with a timer for this for the experience and also because it seems that timers survive automation restarts (but not server restarts which I’m less concerned about).

Here’s my attempt at making this work, however, there’s a bug in it that I can’t seem to find. The automation runs as expected, press the button and the timer starts and the filter comes on, press it again and the air filter turns off and the timer stops.

The problem lies with when the timer finishes (counts down to 0:00) this script seems to start it again and the filter never turns off. The second trigger is what calls this script again, and option #1 should run since the state of the timer is “finished” – although that option never gets picked based on the trace of the script. I’m going to guess that the event that fires when the timer finishes triggers this script, but by the time the “choose” action is run the timer is “idle” not “finished.” But I can’t prove that theory.

So I made another automation that is triggered by the event and it just turns off the air filter. This two-script solution works (after I remove the event trigger for the finished timer from the first one).

I’d like to understand why the single script here isn’t working though:

alias: 'Master bedroom: Air filter 30 minutes (timer)'
description: ''
trigger:
  - device_id: b251a3bee8687b95b2fff6d
    domain: deconz
    platform: device
    type: remote_button_short_press
    subtype: turn_on
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.mbr_air_filter
condition: []
action:
  - choose:
      - conditions:
          # timer finished, just turn off filter
          - condition: state
            entity_id: timer.mbr_air_filter
            state: finished
        sequence:
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c7d
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
      - conditions:
          # filter is off so turn it on and start timer
          - condition: state
            entity_id: timer.mbr_air_filter
            state: idle
        sequence:
          - type: turn_on
            device_id: d8117da0fbe2652aa5d5c7d
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
          - service: timer.start
            data:
              duration: '00:01:00'
            target:
              entity_id: timer.mbr_air_filter
      - conditions:
          # button pressed while timer is active, turn fan off and stop timer
          - condition: state
            entity_id: timer.mbr_air_filter
            state: active
        sequence:
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c7d
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
          - service: timer.cancel
            target:
              entity_id: timer.mbr_air_filter
    default: []
mode: single

I figured it out! It now works perfectly and I don’t need a second event to turn off the filter. It’s all done in this one event:

  1. Press the button to start the 30 minute timer and turn on the air filter.
  2. If I change my mind, press the button to turn the filter off and cancel the timer.
  3. After 30 minutes the air filter turns off automatically.

So here’s some new things I learned here:

  1. The event part of the trigger (when the timer finishes) can be given a “trigger_id”.
  2. In the action → choose section I can look for this trigger_id and act upon it (turn off the filter).
  3. I also learned that in the action → choose options, the first one matches starting at the beginning.

For #3 - I had that option last (trigger_id matches from the event) and it didn’t work, most likely because one of the other options could also match. Once I moved it first everything works perfectly!

Note the two triggers to start this automation, either a button press or the timer finishes.

Here’s the final automation:

alias: 'Master bedroom: Air filter 30 minutes (timer)'
description: ''
trigger:
  - device_id: b251a3bee8687b95b2fff6d7
    domain: deconz
    platform: device
    type: remote_button_short_press
    subtype: turn_on
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.mbr_air_filter
    id: timer_complete
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: timer_complete
        sequence:
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c7
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
      - conditions:
          - condition: state
            entity_id: timer.mbr_air_filter
            state: idle
        sequence:
          - type: turn_on
            device_id: d8117da0fbe2652aa5d5c7
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
          - service: timer.start
            data:
              duration: '00:30:00'
            target:
              entity_id: timer.mbr_air_filter
      - conditions:
          - condition: state
            entity_id: timer.mbr_air_filter
            state: active
        sequence:
          - type: turn_off
            device_id: d8117da0fbe2652aa5d5c7d
            entity_id: switch.mbr_air_filter_on_off
            domain: switch
          - service: timer.cancel
            target:
              entity_id: timer.mbr_air_filter
    default: []
mode: single
2 Likes

It’s still vulnerable to a Reload Timers (which is a required operation if you ever create another timer and it will happen automatically if you create the new timer via the UI) and, of course, a restart. The version I posted above survives a restart and is more concise; it’s simply a more efficient design pattern.

However, if you are adamant about using a timer, you might be interested in implementing the following system for restoring active (and paused) timers after a restart:


NOTE

The following simple tutorial explains the process of transforming a simple automation, vulnerable to restarts, into one that can perform the correct action even after a restart:

1 Like

Actually i think its very strange in HA that you cant do the most common task like a “delay” on a switch very easy. If you need to write 20 lines of code to do a simple “delay” there must be something wrong in HA. All time and delay related tasks should be very easy to do.

Implementing a time delay can vary from a single line to multiple lines; it depends on the application.

1 Like

Hello,
I’m pretty lost.
I would like to do the same thing: push a Hue button will put my radiator in preset_mode comfort for 30min, and come back to preset mode eco. The automation works, but stops after the delay of 30min.

I’ve created an input datetime (input_datetime.smart_button_on_radiateur_sdb), but I don’t know how to use it in your automation.

My current automation:

alias: "[SDB] Smart button hue - ON seche serviette SDB"
description: ""
trigger:
  - device_id: 7e5cf7e64f980dad2bef72477b8773d1
    domain: hue
    platform: device
    type: initial_press
    subtype: 1
    unique_id: 7c729056-ec48-46ef-b051-df22fbaa0aa2
condition: []
action:
  - service: climate.set_preset_mode
    target:
      entity_id: climate.radiateur_sdb
    data:
      preset_mode: comfort
  - delay:
      hours: 0
      minutes: 30
      seconds: 0
      milliseconds: 0
  - service: climate.set_preset_mode
    data:
      preset_mode: eco
    target:
      entity_id: climate.radiateur_sdb
mode: single

Use dmcentire’s example.

It seems very complicated for me in regards of what you have made that should be used for my case:

alias: 'Master Bedroom: Air filter 30 minutes'
description: ''
trigger:
  - device_id: b251a3bee8687b95b2fff6d
    domain: deconz
    platform: device
    type: remote_button_short_press
    subtype: turn_on
  - platform: time
    at: input_datetime.mbr_air_filter_on_off
condition: []
action:
  - variables:
      switch: switch.mbr_air_filter_on_off
      is_on: "{{ is_state(switch, 'on' }}"
  - choose:
      - conditions: '{{ trigger.platform == 'device' and not is_on }}'
        sequence:
          - service: switch.turn_on
            target:
              entity_id: '{{ switch }}'
          - service: input_datetime.set_datetime
            target:
              entity_id: input_datetime.mbr_air_filter_on_off
            data:
              timestamp: "{{ (now() + timedelta(minutes=30).timestamp()) | int(0) }}"
      - conditions: '{{ trigger.platform in ['device', time'] and is_on }}'
        sequence:
          - service: switch.turn_off
            target:
              entity_id: '{{ switch }}'
    default: []
mode: single

So don’t use it. Try dmcentire’s example.

Too complicated for me, :disappointed: my action are preset mode, no on off. And I don’t understand his automation and all sequences