Template access to last state change

I have a couple of automations that trigger after long timers (example below). If HA is restarted for any reason, the timer will never fire. I’m looking for any approach that WILL still fire.

My best idea is to use template binary_sensors that check the time when another sensor was last
changed to a given state, however, I can’t find a way to access state history timestamps from inside a template.

Is that possible?

If not, can someone suggest a new approach?

Example rule:

- id: enter_low_power
    platform: state
    entity_id: binary_sensor.house_occupied
    to: 'off'
      days: 1
  - service: homeassistant.turn_off
    entity_id: group.low_power

I think your probably up the right alley, I’d do a template sensor based on
state==on and (as_timestamp(now()) - as_timestamp(last_changed state)) > 1 day

As now() will no longer update on its own though, you will need another automation to on a per minute/hour basis, run homeassistant.update on it.

Then your enter_low_power automation would just trigger from off to: on

I didn’t know that “last_changed” existed, just hoped there was something like it. So, you’ve helped me there, and pointed out the issue with “now()” before I even hit it.

Thanks a lot!

Also, I found that there are some time/date sensors that can be used instead of “now()”, so that your template will updated on regular basis.

  - platform: time_date
      - 'date_time_iso'

Which gives me the functional binary_sensor template:

    (not states('binary_sensor.house_occupied')) and
    ((as_timestamp(states('sensor.date_time_iso')) - 
      as_timestamp(states.binary_sensor.house_occupied.last_changed)) > 

This should work! It can be slightly off, because the source sensors are templates that are ‘changed’ at system restart, but at least the rules will still fire eventually.

I do feel like there is an opportunity to simplify this a bit, but I can’t seem to get it to work, even after way too much time researching.

The time sensor’s value is a string, not a datetime object. last_changed IS a datetime object. If I could
convert both values to datetime, it would be something like:

    (not states('binary_sensor.house_occupied')) and
    ((states('sensor.date_time_iso') - 
      states.binary_sensor.house_occupied.last_changed) > 

However, the time sensor is just a string, and strptime() appears to convert to a string, not a datetime as documented.

An updated solution. As of 0.117.5, “now()” causes an template to be updated once a minute with no special workarounds.

Since now() and the last_changed attributes are both of type datetime, you can defind the template like this:

  (not states('binary_sensor.house_occupied')) and
   (now() - states.binary_sensor.house_occupied.last_changed) >
1 Like

I don’t know if you have realized it yet but last_changed has the same vulnerability as a timer or using for in a State Trigger. All lose their countdown when Home Assistant is restarted.

The issue with last_changed is that its value is not retained after a restart. It is set to the current time and date at startup. Obviously this screws up any time duration calculations.

If you are interested, I posted a means of restarting active timers interrupted by a restart.

How to make active timers survive a restart.

Could this also work as a condition in an automation rule?
I’ve tried but no success, or might it be that HA no longer supports this?
I read something about now() no longer updating.

Post the automation you created containing the condition that wasn’t successful.

@hapim Note that timers actually do restore after a restart now.

1 Like


As I couldn’t get my statement to work as a condition (always false) while it did work in the dev tools, I moved the condition to my action and now it works.
Please note that for debugging purposes I’ve split the function into several variables such that I could inspect what was going on with my core 2023.11.3.

  - variables:
      lastChanged: "{{ as_timestamp(states.switch.smart_plug_waterboiler.last_changed) }}"
      lastNow: "{{ as_timestamp(now()) }}"
      doIT: >-
        {{ (now() - states.switch.smart_plug_waterboiler.last_changed) >
        timedelta(hours=6) }}
  - if:
      - condition: template
        value_template: "{{ doIT }}"