Hardening Home Assistant Automations Against Unavailable States
If you've been using Home Assistant long enough, you've been woken up at 2am by a notification that shouldn't have fired. A sensor came back online, a service restarted, HA rebooted — and suddenly your phone is buzzing about a “power outage” that never happened.
The root cause is almost always the same: automations that don't account for the full lifecycle of entity states.
This post covers a hardening system I've developed to eliminate those false triggers — including from_state/to_state guards, the availability template pattern, and how to bake these protections into blueprints so every automation gets them for free.
The Problem: Entities Don't Just Toggle
Most automations are written assuming an entity transitions cleanly between on and off. In reality, entities go through a much messier lifecycle:
unavailable → on → off → unavailable → unknown → on
This happens constantly:
- HA restarts and entities briefly report
unavailable - A network blip drops a Zigbee device
- An MQTT service restarts and retained values are republished
- A template sensor recalculates when its source entity comes back online
Each of these transitions can look like a real state change to an automation. Without guards, every one of them can fire your notification, trigger your lights, or run your scripts.
The Two-Guard Pattern
The most important hardening technique is guarding both the from_state and to_state of every trigger:
conditions:
- condition: template
value_template: >
{{ trigger.from_state.state not in ['', 'unavailable', 'unknown'] }}
- condition: template
value_template: >
{{ trigger.to_state.state not in ['', 'unavailable', 'unknown'] }}
Most people only guard to_state — checking that the new state isn't unavailable. But the from_state guard is equally important. Without it, the transition unavailable → on passes right through, which is exactly what happens when a sensor comes back online after a restart.
Both states must be real values for the automation to proceed.
The Availability Template Pattern
For template sensors, the old pattern for handling unavailable sources looked like this:
# Old pattern — prone to issues
state: >
{% set s = states('sensor.some_entity') %}
{% if s in ['unavailable', 'unknown', 'none'] %}
{{ None }}
{% else %}
{{ s == 'on' }}
{% endif %}
The problem is that returning None from a state template just sets the state to the string "None" — not actually unavailable. So you still get state transitions that can trigger automations.
The correct approach is to use the availability template, which is specifically designed for this:
state: >
{{ states('sensor.some_entity') == 'on' }}
availability: >
{{ states('sensor.some_entity') not in ['unavailable', 'unknown'] }}
When availability returns false, the sensor itself becomes unavailable in HA. This means the transition on restart becomes unavailable → on instead of off → on — and the from_state guard catches it.
For sensors with multiple dependencies, use the list pattern:
availability: >
{% set entities = [
'binary_sensor.some_sensor',
'input_boolean.some_boolean',
'switch.some_switch',
] %}
{{ entities | select('is_state', 'unavailable') | list | count == 0
and entities | select('is_state', 'unknown') | list | count == 0 }}
Baking Guards Into Blueprints
Writing these conditions into every automation manually is error-prone and inconsistent. The better approach is to encode them into blueprints so every automation instance gets them automatically.
Here's a hardened binary sensor blueprint:
blueprint:
name: When Binary Sensor is Toggled
description: |
Triggers user-specified actions when a binary sensor changes to on and off.
Ignores unavailable/unknown states and attribute-only changes.
domain: automation
input:
binary_sensor:
name: Binary Sensor
selector:
entity:
domain: binary_sensor
on_action:
name: On Action
default: []
selector:
action: {}
off_action:
name: Off Action
default: []
selector:
action: {}
trigger:
- platform: state
entity_id: !input binary_sensor
condition:
- condition: template
value_template: >
{{ trigger.from_state.state not in ['', 'unavailable', 'unknown'] }}
- condition: template
value_template: >
{{ trigger.to_state.state not in ['', 'unavailable', 'unknown'] }}
action:
- choose:
- conditions:
- condition: state
entity_id: !input binary_sensor
state: "on"
sequence: !input on_action
- conditions:
- condition: state
entity_id: !input binary_sensor
state: "off"
sequence: !input off_action
default: []
mode: single
Every automation using this blueprint is hardened by default. You can create similar blueprints for input booleans, media players, outlets — anything you trigger off regularly.
For even simpler cases, a generic entity state change blueprint covers everything:
blueprint:
name: When Entity State Changes
description: |
Triggers user-specified actions when any entity's state changes.
Ignores unavailable/unknown states and attribute-only changes.
domain: automation
input:
entity:
name: Entity
selector:
entity: {}
actions:
name: Actions
default: []
selector:
action: {}
trigger:
- platform: state
entity_id: !input entity
condition:
- condition: template
value_template: >
{{ trigger.from_state.state not in ['', 'unavailable', 'unknown'] }}
- condition: template
value_template: >
{{ trigger.to_state.state not in ['', 'unavailable', 'unknown'] }}
action: !input actions
mode: single
Automations using this blueprint become pure configuration:
alias: When There is a New Channels DVR Recording
use_blueprint:
path: zackwag/entity_state_change.yaml
input:
entity: sensor.channels_dvr_latest_recording
actions:
- action: script.channels_dvr_new_recording_handler
metadata: {}
When NOT to Use These Guards
Not every automation should use these guards. The pattern is deliberately designed to ignore unavailable — but sometimes you want to know when something goes unavailable.
A backup monitoring automation is a good example:
triggers:
- trigger: state
entity_id: sensor.container_backup_status
to: failed
- trigger: state
entity_id: sensor.container_backup_status
to: unavailable
- trigger: state
entity_id: sensor.container_backup_status
to: unknown
If your backup service goes dark, that's exactly the alert you want. Adding the unavailability guards here would defeat the purpose. Use your judgment — the guards are the right default, but they're not universal.
The Full Defense Stack
For critical alerts like UPS power monitoring, you can combine all of these techniques into a robust defense:
availabilityon template sensors — preventsunavailablefrom masquerading asofffrom_stateguard — blocks transitions out of unavailableto_stateguard — blocks transitions into unavailable- Connectivity condition — suppresses alerts when the monitoring service itself is down
- Persistent last values — prevents MQTT services from republishing retained values on restart
Each layer handles a different failure mode. The result is an alerting system you can actually trust — when your phone buzzes, something real happened.
Summary
The core principles:
- Always guard both
from_stateandto_state— not justto_state - Use
availabilitytemplates instead of returningNonefrom state templates - Encode guards into blueprints so they're applied consistently
- Know when to break the rules — some automations should fire on unavailable
Once you've applied this system across your automations, the 2am false alerts stop. And when a real alert fires, you'll actually pay attention to it.