How to make trigger with "for" work through HA restart?

I just tested it, a trigger with “for” does not work if HA is restarted while it’s waiting:

trigger: state
entity_id:
  - light.bulb_office_andre
from: null
to:
  - "on"
for:
  hours: 0
  minutes: 10
  seconds: 0

I turned on the light, restarted HA and waited 10 minutes, light is still on.

I assumed during restart the state is “unavailable” and so this would just work, but apparently it doesn’t.

Is there a way to make it work?

Whether the timer continues or starts anew doesn’t matter to me, as long as it triggers eventually.

Use of the for option doesn’t survive a Home Assistant restart or the reload of automations.

  • During restart or reload, automations that were awaiting for the trigger to pass, are reset.
  • If for your use case this is undesired, you could consider using the automation to set an input_datetime to the desired time and then use that input_datetime as an automation trigger to perform the desired actions at the set time.

TIMER helpers are used in that scenario, because they will persist when HA is restarted (and the timer duration is longer than it takes to restart HA).

But first, the code as displayed doesn’t turn on or off the light as is, so the FOR command doesn’t do anything. I’d assume this is only part of an automation’s YAML.

I don’t really want to create one helper per light bulb, just to limit the runtime.

I don’t want to create one timer per bulb either.

Sounds like the remaining option is to trigger on homeassistant start and turn off every light that is on… :wink:

It would be nice to be able to create timers on the fly and have them go away when finished.

I wouldn’t want to either.

If you explained what you’re trying to accomplish overall, that might help get you the solution you seek from folks here.

Turn off lights a certain time after they were turned on.

How are your lights turned on?

Does only one light get turned on used at a time?

How many lights, or light groups do you have that you want to turn off automatically after being turned on?

Are you the only one needing light in this location, or do others live with you? Is this action for your living spaces, or elsewhere?

Why do you want the light(s) you turn on turned off automatically after 10 minutes?

How are your lights turned on?

By automations, through the dashboard or via Zigbee groups.

Does only one light get turned on used at a time?

Usually yes.

How many lights, or light groups do you have that you want to turn off automatically after being turned on?

The ones I’m currently working on (I’m consolidating a couple of automations into a single blueprint) are about 10 bulbs.

Are you the only one needing light in this location, or do others live with you? Is this action for your living spaces, or elsewhere?

It’s various lights around the house, yes. They are usually motion controlled, but sometimes that fails for one reason or another (batteries empty, stuff in front of the detector) and then they get turned on manually and forgotten.

Why do you want the light(s) you turn on turned off automatically after 10 minutes?

10 minutes was for testing whether the timer survives a restart. The actual turn-off time will be 2, 6 or 24 hours, depending on the light.

I mean, if there isn’t a universal solution to make for work, I can just use the usual technique: Set the automation to restart and have one choose-branch that does a delay and light.turn_off.

I just wonder what for is for then.

The short answer to your topic title’s question is “You can’t”. Currently there’s no way the internal timer, of a State Trigger’s for, can survive a restart. The automation containing the State Trigger is terminated on shutdown and reset on startup.

You have already specified you don’t want to use any Helpers to keep track of lighting information like when the light was turned on. That leaves you with very little to work with.

Given the constraints, here’s a ‘brute force’ method. Create an automation that triggers on startup, finds all lights that are on, waits 10 minutes, then turns off the lights that are still on. It’s not elegant but respects the requirement of avoiding the use of helpers. It should be used in addition to what you are currently using (a State Trigger with for option).

It does what it’s designed to do while the automation is running.

Restarting Home Assistant will stop and reset all automations. No matter what is running within the automation (its triggers, delay, wait_for_trigger, etc) is terminated.

I use timers for certain rooms, but only because I can have a tendency to experiment and restart HA more than once a day.

I also use an automation to turn off “forgotten” lights, but it doesn’t take into account HA restarts. But using “123 Taras” suggestion (add a HA start trigger), maybe this might suit your needs until a better solution materializes:

alias: Lights On Too Long TOO
description: ""
triggers:
  - trigger: homeassistant
    event: start
  - trigger: state
    entity_id:
      - light.johnny
      - light.mary
      - light.shed_lights_z2m
      - light.study_globe
      - light.study_lights_z2m
      - light.attic_lights_z2m
      - light.garage_lights_z2m
      - light.bedroom_lights_z2m
      - light.bedroom_globe
      - light.shed_lights_z2m
      - light.backyard_lights_z2m
      - light.frontyard_lights_z2m
    to: "on"
    for:
      hours: 1
      minutes: 30
      seconds: 0
conditions: []
actions:
  - action: light.turn_off
    target:
      entity_id: "{{ trigger.entity_id }}"
  - action: notify.persistent_notification
    metadata: {}
    data:
      message: >-
        The "{{states[trigger.entity_id].attributes.friendly_name}}" light
        turned off by LIGHTS ON TOO LONG.
mode: single

Change the Entities and the “for” time, and see how that plays.

1 Like

I don’t mind at all having a separate automation that handles the timer for all the lights. However, since the real timeout will be in the order of hours, this needs to take into account that if a light has been switched off and on again during the time period, it should not (yet) be switched off at the end.

So the for 10 minutes will actually be for several hours? How many hours?

Fortunately, the for option can already handle that situation while the automation is running.

Unfortunately, for the case when the automation is restarted, there’s no record of how long the light was on before the restart. So the duration is zero on startup. If that’s acceptable, then there’s potentially a solution.


EDIT

On second thought, I retract my statement that there might be a potential solution, at least not with what I had in mind. I had devised something that would work for one light but it doesn’t scale to multiple lights.

Perhaps maybe something like this;

when: set a recurring timer (every hour or so)
and if: device/entity light is on, duration # of hours
action: turn light off

@AndreKR
Just as what 123 said, the for in automation would never survive HA restart.

So your only viable option really is to create a helper, also as 123 said.
Yes we know you really don’t want to, but that’s the option you have got.

Any other method would be even more hacky and more complex than the helper method. So if you really want to solve it, I’d recommend you follow the helper route… or don’t do anything & give up & let the automation log errors.

Ok, so there is technically a way you can do this with only creating one single helper, and you can use that same helper for every bulb and for anything else you want to have a persistent “last changed” recorded for. But it’s going to require templates. Lots of templates.

As a bit of a challenge and because I was interested myself, I created a single template sensor that can record the “last changed” timestamp of any entity that you apply a label of “State Tracker” to. Here is what that template sensor is. Since this is a trigger-based template sensor, you have to place this in your configuration.yaml, or your templates.yaml or however you have your config split up:

Click to expand template sensor YAML
templates:
  - triggers:
      - trigger: template
        value_template: >
          {% set entity_list_new = label_entities('state_tracker') %}
          {% set entity_state_list_new = entity_list_new | map('states') | list %}
          {% set states_dict_new = dict(zip(entity_list_new, entity_state_list_new)|list) %}
          {% set states_dict_new_filtered = dict(states_dict_new.items() | rejectattr(1, 'in', ['unavailable', 'unknown'])) %}
          {% set entity_list_old = state_attr('sensor.state_tracker', 'history').keys() | list %}
          {% set entity_state_list_old = state_attr('sensor.state_tracker', 'history').values() | map(attribute='state') | list %}
          {% set states_dict_old = dict(zip(entity_list_old, entity_state_list_old)|list) %}
          {% set states_dict_old_filtered = dict(states_dict_old.items() | selectattr(0, 'in', states_dict_new_filtered.keys()) | list) %}
          {% set entity_mismatch = (entity_list_new|sort != entity_list_old|sort) %}
          {% set states_dict_filtered_mismatch = (states_dict_new_filtered != states_dict_old_filtered) %}
          {{ entity_mismatch or states_dict_filtered_mismatch }}
      - trigger: homeassistant
        event: start
      - trigger: event
        event_type: "state_tracker_refresh"
    sensor:
      - name: State Tracker
        device_class: timestamp
        unique_id: 019c839f-3c7f-74fe-8589-b84436d254c2
        state: "{{ now() }}"
        attributes:
          history: >
            {% set keep_list = label_entities('state_tracker') %}
            {% set t = this.attributes.get('history', {}) %}
            {# remove entities no longer labelled #}
            {% set t = dict(t.items() | selectattr('0', 'in', keep_list)) %}
            {% set ns = namespace(entity_dict={}) %}
            {% for entity in keep_list | expand %}
              {% set state = t.get(entity.entity_id, {}).get('state') %}
              {% set timestamp = t.get(entity.entity_id, {}).get('timestamp') %}
              {% if state != entity.state and entity.state not in ['unavailable', 'unknown'] %}
                {% set state = entity.state %}
                {% set timestamp = entity.last_changed | as_local | string %}
              {% endif %}
              {% set updated_item = {entity.entity_id: {'state': state, 'timestamp': timestamp}} %}
              {% set ns.entity_dict = dict(ns.entity_dict.items(), **updated_item) %}
            {% endfor %}
            {{ dict(ns.entity_dict) }}

What you end up with is a single sensor that has a history of the last time any labeled entity changed, and it ignores unavailable and unknown states and won’t update the timestamp if the same state happens again later. So restarting HA doesn’t affect the history. Here’s what it shows:

This works out really nice, and it is pretty easy to manage since you just have to label things. After adding or removing a label on an entity, you have to either restart, fire the custom event (state_tracker_refresh), or change the state of an already-tracked entity.

But the automation to use that data becomes a bit of a bear.

Here’s an automation example that uses some variables to attempt to make it easier, but it’s still kind of a mess.

Click to expand automation YAML
description: "Entity on Too Long"
mode: single
trigger_variables:
  entity: light.living_room_dimmer
  undesired_state: 'on'
  duration: "00:05:00"
triggers:
  - trigger: template
    value_template: |-
      {{ state_attr('sensor.state_tracker', 'history')[entity]['timestamp'] | as_datetime + as_timedelta(duration) < now()
        and
        state_attr('sensor.state_tracker', 'history')[entity]['state'] == undesired_state
      }}
  - trigger: homeassistant
    event: start
conditions:
  - condition: template
    value_template: |-
      {{ state_attr('sensor.state_tracker', 'history')[entity]['timestamp'] | as_datetime + as_timedelta(duration) < now()
        and
        state_attr('sensor.state_tracker', 'history')[entity]['state'] == undesired_state
      }}
actions:
  - action: persistent_notification.create
    metadata: {}
    data:
      message: "{{ entity ~ ' has been ' ~ undesired_state ~ ' for ' ~ duration }}"

Whether doing all this is really easier than creating a per-entity helper is left for the user to decide…

1 Like

Life would be simpler if Home Assistant didn’t reset every entity’s last_changed property (and last_reported and last_updated) on startup.

As a consequence of automatically flushing the State Machine, you have to either dive into the database to get an entity’s historical data or independently store the data elsewhere, out of reach of the flushing operation (in some form of Helper).

Even if last_ properties were preserved, it wouldn’t change how the triggers work. It would only for conditions (which look at that value).

Triggers create and cancel callbacks in the future.