Graceful Shutdown Sensor

thanks for that last bit, because that (and the use of the ‘==’ operator) was what I am struggling with, especially given what is said in the warning box under:Templating - Home Assistant

I take it you’ve guarded that?

What it’s warning against is that it doesn’t care if the result of the test is true or false, it will always compute the values of if_true, if_false, and if_none.

This will work:

{{ x + 3 if x is defined else 3 }}

This will fail because it will attempt to evaluate x + 3 even when x is undefined.

{{ iif(x is defined, x + 3, 3) }}

The template I posted above isn’t affected by this behaviour because the values of its if_true and if_false are just strings.

2 Likes

thanks again Taras, that explains it very well.
I must admit I was thrown off by the ‘==’ and was looking for a closing ), but now see that the full

this.state|default('unknown') == 'shutdown'

compares to the

is_state('light.kitchen', 'on')

from the docs in the first iif example jut above that warning boxs…

btw, adding a unique_id to that trigger template makes it UI configurable, and I tried to create an extra on/off attribute, so we could easily use that in an automation trigger. Never sure if it updates to the latest state though, so I made this separate binary and that seems to operate correctly:

    sensor:
      - unique_id: graceful_shutdown_sensor
#         name: Graceful shutdown sensor
        state: >
          {% if trigger.event.event_type == 'homeassistant_started' %}
            {{ iif(this.state|default('unknown') == 'shutdown', 'start', 'interrupt') }}
          {% else %}
            shutdown
          {% endif %}
        icon: >
          mdi:restart{{'-alert' if this.state == 'interrupt'}}
        attributes:
          history: >
            {% set current = this.attributes.get('history', []) %}
            {% set new = [{
              "event": trigger.event.event_type[14:],
              "time": now().isoformat() }] %}
            {{ (new + current)[:10] }}

    binary_sensor:
       - unique_id: graceful_shutdown_sensor_binary
 #       name: Graceful shutdown
        state: >
          {{states('sensor.graceful_shutdown_sensor') != 'interrupt'}}

using a first time name during creation, and next commenting that, so the UI can take over.

note ‘interrupt’ at first launch/creation, and binary on, even though state is interrupt…? I have taken out the device_class afterwards though, as it is a positive binary, meaning ‘on’ is good…:

second restart:

I didn’t find a need for it given that a State Trigger with to: interrupt does the job.

yes, you are right.
I tend to create these binary ‘alerts’ so I can easily make frontend conditionals on those too, not only trigger automations. Even use those binary alerts in a container UI binary consisting of only binary_sensors…

but again, maybe I am overdoing things, and I could take several of these out. will scrutinize to see if deletion will bring me (back) some extra efficiency gain.
thanks anyways, its a very fine example of trigger based template and iif usage.
Should be in the docs!

@123 Even after multiples tries and reading of explanations, a plain copy/paste of the above code snippet doesn’t work : in the history attribute, I only see homeassistant_started events, no single homeassistant_stop events.
What could be missing to have it running like expected ?

I’m runing HA 2023.11.2 on Raspberry PI.

Big thanks if you can help !

Thanks for bringing this to my attention.

I have performed a test and was able to replicate your results. There’s no record of the sensor being able to detect Home Assistant’s homeassistant_stop event.

I modified the sensor’s code to use Homeassistant Trigger to detect the shutdown event but it still failed to detect it

  - platform: homeassistant
    event: shutdown

It appears that since I posted my example in March, something changed in Home Assistant that now prevents a Trigger-based Template Sensor from detecting any events involving the shutdown process.

1 Like

I’ve also tried the homeassistant trigger with same result.

The positive of this is that we are now at least 2 sharing this issue.
What action do you recommend ? Bug reporting ?

Thanks,

missed that completely too.

its all the more frustrating, because now the state never changes from interrupt, rendering the sensor useless. While it was such an elegant solution.

Taras, will you file an issue on the matter? Maybe there’s an easy fix in core…

It seems like a bug. I wish I knew what changed that caused a Trigger-based Template entity to lose the ability to detect the shutdown event. Maybe the Template integration is already disabled by the time the shutdown event is fired.

just so this is checked in core: trigger based template sensor no longer triggers on 'shutdown' · Issue #104116 · home-assistant/core · GitHub

2 Likes

Thanks for posting it. Lately I have far less free time to contribute to this forum, reporting issues, etc.

I’m hoping the cause of the problem isn’t a design decision to shutdown the Template integration before issuing the homeassistant_stop event.

I tested 3 workarounds:
1 - Automation + MQTT: Create an MQTT sensor and update/check its status using start/shutdown events.
Problem: the sensor is not being read at start, despite the MQTT state being correct, the sensor remains unknown, causing the automation to set the state to interrupted. Any idea?

2 - Automation + text helper: Same logic as above.
Problem: Strange behavior, it always returns to the state before it was shutdown, it seems that the state set by the automation is not persistent.

3 - Automation + shell command + python_script:
It seems to be working well:

It seems that homeassistant_stop event works again, at least in 2023.12.1.
There is one more caveat when using a template sensor, though. Home Assistant itself does not immediately write the new sensor state to the disk after it has been changed (e.g. set to start after a graceful reboot). If a power failure happens at this moment, upon the next startup the state of the sensor will be start instead of ‘interrupt’ because the recent stored value in entity registry was shutdown.

That’s good news. I’ll check it when I upgrade from 2023.11.

Has this ever happened to you?

Because this alleged behavior hasn’t been observed and reported since the example was posted in March 2023.

in 2024.1 beta a pr was made to the Trigger Home Assistant shutdown automations right before the stop event instead of during it by tetele · Pull Request #91165 · home-assistant/core · GitHub

short discussion suggested to use

trigger:
  - platform: homeassistant
    event: shutdown
  - platform: homeassistant
    event: start

so hopefully this would be fixed now?

can confirm this to work (note I added a trigger.id as there are no trigger variables to the homeassistant platform to use?). See the top 2 attributes in the screen tp be the result of this latest version:

template:

  - trigger:
#       platform: event
#       event_type:
#         - homeassistant_started
#         - homeassistant_stop
      - platform: homeassistant
        event: shutdown
        id: shutdown
      - platform: homeassistant
        event: start
        id: start

    sensor:

      - unique_id: graceful_shutdown_sensor
        state: >
          {% if trigger.id == 'start' %}
            {{iif(this.state|default('unknown') == 'shutdown','start','interrupt')}}
          {% else %}
            shutdown
          {% endif %}
        icon: >
          mdi:restart{{'-alert' if this.state == 'interrupt'}}
        attributes:
          history: >
            {% set current = this.attributes.get('history',[]) %}
            {% set new = [{
              "event": trigger.id,
              "time": now().isoformat()}] %}
            {{(new + current)[:10]}}

The shutdown trigger is fixed in 2024.1 and should be used preferably. do keep in mind what Frenck mentioned in the beta:

unless the automation takes over 20 seconds, in that case, the automation is aborted to not block the shutdown process

3 Likes

Hi Marius,
Thanks for making this suggestion.
I have implemented your exact code but the state never reaches “interrupt” even though I just pull the plug on the power supply.
Also it seems your screenshot with history does not match the above code since it shows “event: stop” but the trigger ids only include “start” and “shutdown”. Just curious?

In my case, I seem to get a shutdown state briefly before the start state and this causes the interrupt to not be triggered. When I do normal reboot, the shutdown state duration is about 1 min - 1.5 min, but when I just pull the plug on the power supply and then connect it, I still get a shutdown state but it only lasts around 20 sec.

Any idea why this happens? And is anyone else seeing this behavior?

hmm, seems you are right… tbh, I hadn’t even realized that, and cant remember now how that would have been an option before, I need to go to the backups of that time.

currently I also only see this:

edit

@emkaywest just read the thread above, on the change of the trigger events. The previous screenshot I posted was made when the former events still were like that.

summary:

template:

  - trigger:
#       platform: event
#       event_type:
#         - homeassistant_started
#         - homeassistant_stop
      - platform: homeassistant
        event: shutdown
        id: shutdown
      - platform: homeassistant
        event: start
        id: start

need to get back to this once again, as today I realized a couple of details I hadn’t noticed before (luckily there aren’t too many interrupts…)

given the fact I use an icon template with the this state, the entity is nit showing what I want it to in case of the state interrupt.

    sensor:

      - unique_id: graceful_shutdown_sensor
        state: >
          {% if trigger.id == 'start' %}
            {{iif(this.state|default('unknown') == 'shutdown','start','interrupt')}}
          {% else %}
            shutdown
          {% endif %}
        icon: >
          mdi:restart{{'-alert' if this.state == 'interrupt'}}
        attributes:
          history: >
            {% set current = this.attributes.get('history',[]) %}
            {% set new = [{
              "event": trigger.id,
              "time": now().isoformat()}] %}
            {{(new + current)[:10]}}

as TheFes explained to me once again:

the this variable is created on trigger for trigger based template sensors
so before the new state template is rendered, and will therefor have the “previous” state

and that is not what I hoped it would do, so I guess we can only repeat the state template and then use that for mdi: icon,

        icon: >
          {% if trigger.id == 'start' %}
            mdi:{{iif(this.state|default('unknown') == 'shutdown','restart','restart-alert')}}
          {% else %}
            mdi:power
          {% endif %}

or, truly self-reference:

{% set restart = states('sensor.graceful_shutdown_sensor') %}
mdi:restart{{'-alert' if restart == 'interrupt'}}

Ive now added the sensor entity to my recorder, so at least the system records it in history etc, but hope to adapt the config to show it in the history attribute too
also, I just discovered the the ‘interrupt’ is Not recorded in the history attribute. Checking the template again makes that obvious, but I hadn’t realized before.

You’ve got it working now, but if it helps here’s a little info on self-referencing:

As TheFes explained, the this variable is defined at the trigger for trigger-based template sensors. But keep in mind the state machine isn’t updated until the entire sensor has been rendered, which includes both state and attributes. So in a trigger-based template sensor’s state or attribute definition, there is no difference between using this compared to states('sensor.<the sensor being defined>') because the state machine will still have the value of the sensor as it was previously rendered, which is exactly what is in this.

The options to get the new state (or new attribute) are either:
Option 1: Duplicate the template code to calculate the new state (or new attribute)
Option 2: Re-render the entire sensor by adding itself as a trigger

Option 2 can be hazardous if the template will change every time it renders, because you’ll quickly put your HA instance into a death spiral. But if you avoid doing something stupid it will only update twice in rapid succession. On the 3rd render nothing should change, so it won’t trigger again. This may sound dangerous but this is already exactly how state-based template sensors work when you use this in them.

To wrap this story up: the order of how states and attributes within a template sensor are rendered is irrelevant because no matter how you attempt to self-reference, you will be referencing stale data. You can’t reference the newly-rendered data without re-rendering the entire template sensor.