Restore active/paused timers after a restart

Notice of Deprecation

Thanks to the contribution of raman325, starting from Home Assistant version 2022.4, an active or paused timer entity can be restored after a restart. Therefore the restoration technique described in this topic is no longer needed.

This topic remains for historical purposes, in case someone is curious to learn more about the scripting techniques it used to restore active/paused timers. However no further support for it will be provided now that timer restoration is a native feature.


Introduction

Active and paused timers do not survive a restart of Home Assistant (EDIT: prior to version 2022.4). On startup, previously active timers are set to idle and donā€™t resume their (interrupted) countdown. Similarly, paused timers are set to idle and reset to their original duration so their elapsed time is lost.

The following strategy automatically restores and resumes active timers upon startup and restores any paused timers. It is best suited for timers whose duration is several minutes or longer (i.e. timerā€™s duration is longer than the time it takes Home Assistant to restart).

It consists of:

  • Two input_text entities to store the state of active and paused timers.
    • input_text.timers_active
    • input_text.timers_paused
  • Two automations. One saves the state of active and paused timers. The other restores them at startup (only active timers are also resumed) .
    • automation.timers_save
    • automation.timers_restore

Step 1 - Create Input_Text entities

Create the following two input_text entities in your configuration.yaml file:

Click to show input_text entities
input_text:
  timers_active:
    name: Active Timers
    max: 255
  timers_paused:
    name: Paused Timers
    max: 255
  1. Execute Configuration > Server Controls > Check Configuration and ensure there are no syntax errors.
  2. Execute Configuration > Server Controls > Reload Input Texts

If you prefer to create them via Configuration > Helpers, ensure you set Maximum Size to 255 characters. Do not change the names of the two entities (timers_active and timers_paused) because they are referenced by the automations.

Step 2 - Create automations

Add the following two automations to wherever you keep them (configuration.yaml or automations.yaml).

automation.timers_save

The first automation is executed whenever a timer changes state and saves the states of all active and paused timers. This automation requires customization:

  1. Replace the State Triggerā€™s entities with the names of your timers.
  2. Replace the entities in the timers variable with the names of your timers. This list should match the one you have in the State Trigger.
Click to show automation.timers_save
- alias: 'Timers Save'
  mode: queued
  trigger:
    platform: state
    entity_id:
    - timer.timer_1
    - timer.timer_2
    - timer.timer_3
  action:
  - variables:
      timers:
      - timer.timer_1
      - timer.timer_2
      - timer.timer_3
      modes:
      - active
      - paused
  - repeat:
      count: 2
      sequence:
      - variables:
          mode: '{{ modes[repeat.index-1] }}'
      - service: input_text.set_value
        data:
          entity_id: 'input_text.timers_{{ mode }}'
          value: >
            {% set ns = namespace(timers = []) %}
            {% for t in expand(timers) | selectattr('state', 'eq', mode) | list  %}
              {% set d = t.attributes.remaining.split(':') | map('int') | list %}
              {% set s = d[0]*3600 + d[1]*60 + d[2] + (t.last_changed.timestamp()|int if mode == 'active' else 0) %}
              {% set ns.timers = ns.timers + ['{} {}'.format(t.object_id, s)] %}
            {% endfor %}
            {{ ns.timers | join(',')}}

automation.timers_restore

The second automation is triggered at startup and resumes/restores all (previously) active and paused timers. This automation does not require customization.

Click to show automation.timers_restore
- alias: 'Timers Restore'
  mode: single
  trigger:
    platform: homeassistant
    event: start
  action:
  - variables:
      modes:
      - active
      - paused
  - repeat:
      count: 2
      sequence:
      - variables:
          mode: '{{ modes[repeat.index-1] }}'
          timers_text: "{{ states('input_text.timers_' ~ mode) }}"
      - choose:
        - conditions: '{{ timers_text | length > 0 }}'
          sequence:
          - variables:
              timers: "{{ timers_text.split(',') }}"
          - repeat:
              count: '{{ timers | count }}'
              sequence:
              - variables:
                  t: '{{ timers[repeat.index-1].split() }}'
                  id: 'timer.{{t[0]}}'
                  d: "{{ t[1]|int - (now().timestamp()|int if mode == 'active' else 0) }}"
              - condition: template
                value_template: '{{ d > 0 }}'
              - service: timer.start
                data:
                  entity_id: '{{ id }}'
                  duration: '{{ d }}'
              - condition: template
                value_template: "{{ mode == 'paused' }}"
              - service: timer.pause
                data:
                  entity_id: '{{ id }}'
  1. Execute Configuration > Server Controls > Check Configuration and ensure there are no syntax errors.
  2. Execute Configuration > Server Controls > Reload Automations

Step 3 - Test

Restart Home Assistant and perform a test:

  1. Start a timer with a duration of 2 minutes (or more if it takes a long time for your instance of Home Assistant to restart).
  2. After youā€™ve confirmed the timer is running, restart Home Assistant.
  3. After it restarts, check the timer in the States page. It should be active and have a shorter duration.

NOTES

When active and paused timers are restored, their duration will not be the same as prior to the interruption/restart.

Click to reveal details of how timers are restored.

New duration for active timers

After a restart, active timers are restored and resumed with a new, shorter duration, representing the remaining balance of their original duration. This shorter time is also reduced by the time Home Assistant was off (during a restart or a power-failure).

  • Example 1:
    If the timerā€™s duration was originally 10 minutes and it was interrupted at the 5 minute mark, it will resume at 5 minutes minus whatever time it took for Home Assistant to restart. If it takes Home Assistant a full 2 minutes to restart, the timerā€™s new default duration will be 5 - 2 = 3 minutes. The next time you start this timer, you will have to set its duration back to 10 minutes otherwise it will use 3 minutes.

  • Example 2:
    Assume a timerā€™s duration is 20 minutes and it counts down to 15 minutes when Home Assistant stops due to a power-failure. Home Assistant restarts 5 minutes later. Upon startup the timer is started with a duration of 10 minutes (not 15) because it takes into account the 5 minutes expended during the power-failure.

  • Example 3:
    Assume a timerā€™s duration is 20 minutes and it counts down to 10 minutes when Home Assistant stops due to a power-failure. Home Assistant restarts 15 minutes later. Upon startup the timer is not restarted because, if there was no power-failure, it would have expired 5 minutes ago.

New duration for paused timers

After a restart, active timers are restored (but not resumed) with a new, shorter duration, representing the remaining balance of their original duration. This shorter time is not reduced by the time Home Assistant is off.

  • Example 1:
    If a timerā€™s duration was originally 10 minutes and it was paused at the 5 minute mark, it will be restored with a duration of 5 minutes (regardless of how much time it takes for Home Assistant to restart, even including a long power-failure). When you start the paused timer, it will complete counting down its remaining 5 minutes. However, its default duration will now be 5 minutes (not its original 10 minutes). You will have to set its duration back to 10 minutes whenever you start it again.
34 Likes

Unbelievable nobody else replied yet, but a huge thanks !

Many thanks for posting this solution. One comment: example 3 for the new duration of an active timer covers a case where the timer.finished event isnā€™t fired because the timer is not restarted. So any automations that rely on that event will need some work.

For example, I have a vacuum that starts a 1 hour timer when kicked off and uses timer.finished to stop. For my case I also trigger on the battery level dropping below 20%, so my automation requires no additional work, but anyone using your solution needs to keep this in mind.

True but anyone who does not use this solution faces the same predicament (and then some because none of their timers will be restarted so none will produce a timer.finished event after startup).

As a workaround start the timer for 1 second ?

Feel free to add it to your instance of the code.

FWIW, a long time ago, someone submitted a PR to enhance timers. In addition to restoring an interrupted timer, it also introduced the concept of a ā€˜grace periodā€™ where additional time could be appended to a restored timer. In effect, it attempted to address the edge-case you described. Ultimately, the PR was rejected by the development team and the ā€˜grace periodā€™ was one of the contentious issues.

Hi @123 ,

First off I want to thank you for this solution. This is exactly what I require for my set up.

I would like to add @devastator 's suggestion of resetting the timer to 1 second if on reboot the timers have in fact finished and there is no time value to restore. I get a lot of power outages where I live and this would be very useful.

Could you please help me figure out what lines of code I would need to add to your code in order to make this work?

Thank you for your time and help,

  • Kal

Send a PM to devastator and ask if they ever implemented it.

Thanks for the reply @123 . Iā€™ll try that.

@devastator , have you by chance already implemented your suggestion of resetting the timer to 1 second?

Thanks folks,

  • Kal

No net yet ā€¦

Please forgive my ignorance as I am completely guessing here:

Would changing the 0 to a 1 in the this line of the automation.timers_restore code be a good start?

d: ā€˜{{ t[1]|int - (now().timestamp()|int if mode == ā€˜ā€˜activeā€™ā€™ else
0) }}ā€™

I tried running a test with this change but it didnā€™t seem to be successful. Either Iā€™m completely off track, running my test wrong, or there could be another line of code needed. Thoughts?

  • Kal

This is untested but I think it will do what you want. If an active timer has negative remaining time (meaning there is no remaining duration left), it is restarted with a duration set to 1 second so that it can be allowed to quickly run and finish. This allows the timerā€™s completion to trigger any automations you may have that are listening for the timerā€™s finished event

                   d: >
                     {% set ts = t[1]|int %}
                     {% if mode == 'active' %}
                       {% set ts = ts - now().timestamp()|int %}
                       {{ ts if ts > 0 else 1 }}
                     {% else %}
                       {{ ts }}
                     {% endif %}

I would advise using this judiciously because briefly running a timer beyond its expiration time may have unforeseen consequences.

Hi, thanks a lot for this. Iā€™ve tried to create the first automation ā€˜Timers saveā€™ and I get a ā€˜Message malformed: expected dictionaryā€™ when I tried to save it using the yaml editor from the Automations menu on the UI.
Thank you all very much for your help.

I donā€™t use the Automation Editor because it has a few quirks that can negatively impact an automationā€™s syntax (i.e. it can change things and not always for the better). That might be the cause of the error you encountered. I created both automations with a text editor, confirmed they were valid with Check Configuration, and they have been operating properly on my system (for many months).

1 Like

Whouaou, this is great, thank you for this solution. I just implemented it (including the 1sec negative time) on my swimming pool pump timers and it works great so far.

@123 Simply fantastic. Exactly what I needed for some timers on a remote (not-in-my-house), unattended Home Assistant project. That is some special Jinja-fu you have going on there, including the 1 second duration for expired timers. Thanks for sharing.

Thanks for sharing this. I always forget, however, to add new timers to the automation. Is it possible to enumerate them rather than have to input them rather than having to add each one each time?

EDIT: I think I found my answer. :neutral_face:

Hey Taras. This solution works like a charm for home assistant restart. But it doesnā€™t work for reloads e.g. homeassistant.reload_config_entry. Any idea how we could make it work?

It was designed exclusively for restoring active/paused timers after a restart.

If you want it to perform that function for other events, feel free to add whatever additional triggers you want to automation.timers_restore.

Hey Taras. Actually it does work for the entity reloads as well. I had forgotten to add that specific timer that I was testing to the timer automations you asked us to create. Upon adding it there the timer kept running even after an entity reload. Sorry to bother you. Great work with this concept. It solved so many of my automation problems!!