Automations
Home Assistant automations, ready to copy
Short versions of the automations from the blog. The entity IDs are placeholders, so swap in your own. The full story, including what broke along the way, is in the linked post.
Pause the TV when a phone rings
Pauses the TV the moment a phone rings and resumes two seconds after the call ends, but only if the automation was the one that paused it.
- Trigger
- The phone state sensor from the Companion app changes to ringing, in_call or offhook.
- Conditions
- The TV is on, and the helper input_boolean.tv_was_playing is off.
- Actions
- Set the helper, send pause, wait for idle (two hours at most), wait two seconds, send play, clear the helper.
- Requires
- Home Assistant Companion app on the phone, the Android TV integration and one input_boolean helper.
Automation
alias: TV — Pause on phone call
trigger:
- platform: state
entity_id: sensor.your_phone_phone_state
to: [ringing, in_call, offhook]
condition:
- condition: state
entity_id: remote.living_room_tv
state: "on"
- condition: state
entity_id: input_boolean.tv_was_playing
state: "off"
action:
- action: input_boolean.turn_on
target:
entity_id: input_boolean.tv_was_playing
- action: remote.send_command
target:
entity_id: remote.living_room_tv
data:
command: MEDIA_PAUSE
- wait_for_trigger:
- platform: state
entity_id: sensor.your_phone_phone_state
to: idle
timeout: "02:00:00"
continue_on_timeout: true
- delay:
seconds: 2
- condition: state
entity_id: input_boolean.tv_was_playing
state: "on"
- action: remote.send_command
target:
entity_id: remote.living_room_tv
data:
command: MEDIA_PLAY
- action: input_boolean.turn_off
target:
entity_id: input_boolean.tv_was_playing The full story: Pausing the TV Automatically for Regular Calls and Messenger
Heating off when a window opens (blueprint)
Turns a radiator off once a window has been open for a minute, and puts back its exact previous state when the window closes. Built as a blueprint, so the next room takes about 30 seconds.
- Trigger
- The window has been open for the chosen number of minutes, or the window closes.
- Conditions
- None. A quick airing never fires, because the open trigger waits for the delay.
- Actions
- On open: snapshot the radiator into a scene, then set it to off. On close: restore the scene.
- Requires
- A window sensor with device_class window and a climate entity. Tested with first-generation Mill radiators.
Blueprint: /config/blueprints/automation/homeassistant/window_heating_control.yaml
blueprint:
name: "Window → Heating off"
domain: automation
input:
window_sensor:
selector:
entity:
domain: binary_sensor
device_class: window
climate_entity:
selector:
entity:
domain: climate
delay_minutes:
default: 1
selector:
number:
min: 0
max: 10
unit_of_measurement: min
mode: restart
trigger:
- platform: state
entity_id: !input window_sensor
to: "on"
for:
minutes: !input delay_minutes
id: opened
- platform: state
entity_id: !input window_sensor
to: "off"
id: closed
action:
- choose:
- conditions:
- condition: trigger
id: opened
sequence:
- action: scene.create
data:
scene_id: "{{ this.entity_id | replace('automation.', '') }}"
snapshot_entities:
- !input climate_entity
- action: climate.set_hvac_mode
target:
entity_id: !input climate_entity
data:
hvac_mode: "off"
- conditions:
- condition: trigger
id: closed
sequence:
- action: scene.turn_on
target:
entity_id: "scene.{{ this.entity_id | replace('automation.', '') }}"
continue_on_error: true One instance per room
- alias: "Window → Heating: bedroom"
use_blueprint:
path: homeassistant/window_heating_control.yaml
input:
window_sensor: binary_sensor.bedroom_window
climate_entity: climate.bedroom
delay_minutes: 1 The full story: A Home Assistant Blueprint That Stops the Heat When a Window Opens
Outdoor lights on at sunset, off at midnight
Turns the outdoor lights on 15 minutes after sunset and off at midnight, all year, without adjusting a timer when the seasons change.
- Trigger
- Sunset plus 15 minutes for on, and 00:00 for off.
- Conditions
- None.
- Actions
- Turn the outdoor lights on at full brightness, then off again at midnight.
- Requires
- Any lights in Home Assistant. In my setup that is Govee and LEDVANCE.
Two automations
- alias: Outdoor lights — Sunset on
trigger:
- platform: sun
event: sunset
offset: "+00:15:00"
action:
- action: light.turn_on
target:
entity_id:
- light.outdoor_1
- light.outdoor_2
data:
brightness_pct: 100
- alias: Outdoor lights — Midnight off
trigger:
- platform: time
at: "00:00:00"
action:
- action: light.turn_off
target:
entity_id:
- light.outdoor_1
- light.outdoor_2 The full story: Govee and LEDVANCE Outdoor Lights on a Sunset Schedule in Home Assistant
Circadian lighting from the sun’s elevation
Every 30 minutes, and whenever a lamp is switched on, the colour temperature follows the sun: cool at noon, warm at dusk. Lamps that are off stay off.
- Trigger
- Every 30 minutes, or a lamp has been on for five seconds.
- Conditions
- None in the trigger. Inside the loop, only lamps that are already on are touched.
- Actions
- Turn the sun’s elevation into 2200 to 6500 K and set that colour temperature on each lamp that is on.
- Requires
- Lamps with colour temperature support (WiZ in my setup) and the built-in sun entity.
Automation
alias: Circadian lighting
trigger:
- platform: time_pattern
minutes: "/30"
- platform: state
entity_id:
- light.lamp_1
- light.lamp_2
- light.lamp_3
- light.lamp_4
to: "on"
for: "00:00:05"
variables:
elevation: "{{ state_attr('sun.sun', 'elevation') | float(0) }}"
color_temp_k: >
{{ [[5000 if elevation > 45
else ((3500 + (elevation - 15) / 30 * 1500) | int) if elevation > 15
else ((2700 + elevation / 15 * 800) | int) if elevation > 0
else 2200,
2200] | max,
6500] | min }}
action:
- repeat:
for_each:
- light.lamp_1
- light.lamp_2
- light.lamp_3
- light.lamp_4
sequence:
- if:
- condition: template
value_template: "{{ is_state(repeat.item, 'on') }}"
then:
- action: light.turn_on
target:
entity_id: "{{ repeat.item }}"
data:
color_temp_kelvin: "{{ color_temp_k }}" The full story: WiZ Circadian Lighting That Tracks the Sun's Color Temperature
Heating down to 17 °C when everyone is out
Drops every Tado zone to 17 °C when the last person leaves and hands control back to Tado’s own schedule when the first one comes home. The presence comes from your own logic, not from Tado’s geofencing.
- Trigger
- The household group changes to not_home, or back to home.
- Conditions
- None.
- Actions
- Away: set every zone to 17 °C. Home: set every zone back to auto.
- Requires
- The Tado integration, one climate entity per zone and a group or person entity that shows who is home.
Two automations
- alias: Tado — Away setback
trigger:
- platform: state
entity_id: group.household_members
to: not_home
action:
- action: climate.set_temperature
target:
entity_id:
- climate.zone_living_room
- climate.zone_bedroom
- climate.zone_office
data:
temperature: 17
- alias: Tado — Restore on return
trigger:
- platform: state
entity_id: group.household_members
to: home
action:
- action: climate.set_hvac_mode
target:
entity_id:
- climate.zone_living_room
- climate.zone_bedroom
- climate.zone_office
data:
hvac_mode: auto The full story: A Tado Heating Dashboard With card_mod That's Actually Worth Looking At
Alert when a window is open and the heating runs
Sends a push when a window has been open for 20 minutes and the zone is still heating. It catches the window nobody closed, and costs nothing to run.
- Trigger
- A window sensor has been on for 20 minutes.
- Conditions
- The matching climate entity reports hvac_action heating.
- Actions
- Send a push with the room name, taken from the entity ID.
- Requires
- Tado window sensors named binary_sensor.zone_<room>_window, matching climate.zone_<room> entities and a notify service.
Automation
alias: Tado — Open window alert
trigger:
- platform: state
entity_id:
- binary_sensor.zone_living_room_window
- binary_sensor.zone_bathroom_window
to: "on"
for:
minutes: 20
condition:
- condition: template
value_template: >
{{ state_attr(trigger.entity_id | replace('binary_sensor.', 'climate.') | replace('_window', ''), 'hvac_action') == 'heating' }}
action:
- action: notify.all_devices
data:
title: "Window open — heating running"
message: >
{{ trigger.entity_id
| replace('binary_sensor.zone_', '')
| replace('_window', '')
| replace('_', ' ')
| title }} has been open for 20 minutes. The full story: A Tado Heating Dashboard With card_mod That's Actually Worth Looking At
Vacuum when you leave, dock when you return
The vacuum starts five minutes after you leave and goes back to the dock when you come home. The five minutes filter out GPS blips, so it never starts because your phone lost signal for a moment.
- Trigger
- The person entity is not_home for five minutes, or changes to home.
- Conditions
- Start: between 09:00 and 18:00 and the vacuum is docked. Dock: the vacuum is cleaning.
- Actions
- Start the vacuum, or send it back to base.
- Requires
- Any vacuum entity (Roborock in my setup) and the Companion app for presence.
Two automations
- alias: Vacuum — Clean on departure
trigger:
- platform: state
entity_id: person.your_name
to: not_home
for:
minutes: 5
condition:
- condition: time
after: "09:00:00"
before: "18:00:00"
- condition: state
entity_id: vacuum.your_vacuum
state: docked
action:
- action: vacuum.start
target:
entity_id: vacuum.your_vacuum
- alias: Vacuum — Dock on return
trigger:
- platform: state
entity_id: person.your_name
to: home
condition:
- condition: state
entity_id: vacuum.your_vacuum
state: cleaning
action:
- action: vacuum.return_to_base
target:
entity_id: vacuum.your_vacuum The full story: What's Actually Useful About the Roborock Map in Home Assistant
Push notification when the mailbox opens
Every phone gets a push when the mailbox door opens. A reed switch only knows open or closed, so there are no false alarms.
- Trigger
- The mailbox sensor changes from off to on.
- Conditions
- None.
- Actions
- Send a notification to the notify group.
- Requires
- A LoRa mailbox sensor (a MailBoxGuard in my setup) and a notify group for your phones.
Automation
alias: Mailbox — Notify on delivery
trigger:
- platform: state
entity_id: binary_sensor.mailboxguard
from: "off"
to: "on"
action:
- action: notify.all_devices
data:
title: "Mail in the mailbox"
message: "Something was delivered." The full story: MailBoxGuard: LoRa Mailbox Sensor 200 Metres from the House
Living room lights follow the TV
When a film starts, the ceiling lights go off and the accent lights drop to 15 %. Pause or switch off and everything returns to full brightness. No scene button and no voice command.
- Trigger
- The TV media player changes to playing, paused, or idle/off.
- Conditions
- The time is after 18:00.
- Actions
- Playing: ceiling lights off, accent lights at 15 % and 2200 K. Paused or off: all lights at 100 % and 4000 K.
- Requires
- A media player entity for the TV (the Android TV integration in my setup) and lights with colour temperature. Mode restart stops a quick pause and play from flashing the room.
Automation
alias: Living room lights follow the TV
mode: restart
trigger:
- platform: state
entity_id: media_player.living_room_tv
to: playing
id: playing
- platform: state
entity_id: media_player.living_room_tv
to: paused
id: paused
- platform: state
entity_id: media_player.living_room_tv
to: [idle, "off"]
id: stopped
condition:
- condition: time
after: "18:00:00"
action:
- choose:
- conditions:
- condition: trigger
id: playing
sequence:
- action: light.turn_off
target:
entity_id:
- light.ceiling_1
- light.ceiling_2
- action: light.turn_on
target:
entity_id:
- light.accent_1
- light.accent_2
data:
brightness_pct: 15
color_temp_kelvin: 2200
- conditions:
- condition: trigger
id: [paused, stopped]
sequence:
- action: light.turn_on
target:
entity_id:
- light.ceiling_1
- light.ceiling_2
- light.accent_1
- light.accent_2
data:
brightness_pct: 100
color_temp_kelvin: 4000 The full story: WiZ Lights That React to the TV Without a Cinema Mode Button
Run the dishwasher in the cheapest hour
One script finds the cheapest remaining hour in today’s spot prices, sets the finish time to match and starts the machine. On a typical Danish weekday that is worth one to two kroner per wash.
- Trigger
- None. You start the script from a dashboard button once the machine is loaded.
- Conditions
- None. Only hours from now onwards are considered.
- Actions
- Read today’s prices, pick the cheapest hour, add the programme length, set the finish time and press start.
- Requires
- Energi Data Service (HACS) and a connected dishwasher with a finish-time select and a start button. Remote start must be enabled on the machine itself.
Script, started from a dashboard button
alias: Dishwasher — Schedule cheapest hour
sequence:
- variables:
prices: "{{ state_attr('sensor.energi_data_service', 'raw_today') | default([]) }}"
now_h: "{{ now().hour }}"
duration_h: 2
best_hour: >
{% set ns = namespace(hour=none, price=999) %}
{% for h in prices %}
{% if as_datetime(h.hour).hour >= now_h | int and h.price < ns.price %}
{% set ns.price = h.price %}
{% set ns.hour = h.hour %}
{% endif %}
{% endfor %}
{{ ns.hour }}
- action: select.select_option
target:
entity_id: select.dishwasher_finish_at
data:
option: >
{{ (as_datetime(best_hour) + timedelta(hours=duration_h)).strftime('%H:%M') }}
- action: button.press
target:
entity_id: button.dishwasher_start The full story: Scheduling a Bosch Dishwasher in Home Assistant by Electricity Price
Push the shopping list when you reach the store
When a phone enters a store zone, the automation reads the shopping list and pushes the items for that store. It stays quiet if nothing on the list belongs there.
- Trigger
- A phone enters one of the store zones.
- Conditions
- At least one open list item has a description that starts with the store name.
- Actions
- Look up the store name, fetch the open items, filter them by store and send a push with the list.
- Requires
- The eTilbudsavis integration with the patched todo.py from the post, one zone per store and the Companion app. Any to-do list works if the store name sits in the description.
Automation, two stores shown
alias: Shopping list — Push at the store
trigger:
- platform: zone
entity_id:
- device_tracker.phone_1
- device_tracker.phone_2
zone: zone.supermarket_a
event: enter
- platform: zone
entity_id:
- device_tracker.phone_1
- device_tracker.phone_2
zone: zone.supermarket_b
event: enter
variables:
store_lookup:
zone.supermarket_a: "Supermarket A"
zone.supermarket_b: "Supermarket B"
store_name: "{{ store_lookup[trigger.zone.entity_id] | default('') }}"
action:
- action: todo.get_items
target:
entity_id: todo.shopping_list
data:
status: needs_action
response_variable: todo_result
- variables:
found: >
{% set ns = namespace(items=[]) %}
{% for item in todo_result['todo.shopping_list']['items'] | default([]) %}
{% if store_name != '' and item.description | default('') | regex_match(store_name) %}
{% set ns.items = ns.items + [item.summary] %}
{% endif %}
{% endfor %}
{{ ns.items }}
- condition: template
value_template: "{{ found | count > 0 }}"
- action: notify.all_devices
data:
title: "{{ store_name }}: {{ found | count }} item(s) on the list"
message: "{{ found | join('\n') }}" The full story: eTilbudsavis in Home Assistant: Sorted by Store, with Proximity Alerts
Spoken morning briefing at 06:00
Reads out the weather, today’s calendar, waste collection and the electricity price on a kitchen speaker. This is a trimmed version of the one I run, which also covers traffic and the car.
- Trigger
- The time is 06:00.
- Conditions
- None. Add a weekday condition if you want quiet weekends.
- Actions
- Fetch today’s events, build the text in a template and speak it with tts.speak.
- Requires
- A calendar, a weather entity, a TTS engine and a media player. The waste and price sensors are optional, so cut those lines if you have none.
Automation, trimmed version
alias: Morning briefing — spoken at 06:00
mode: single
trigger:
- platform: time
at: "06:00:00"
action:
- action: calendar.get_events
target:
entity_id: calendar.family
data:
start_date_time: "{{ today_at('00:00') }}"
end_date_time: "{{ today_at('23:59') }}"
response_variable: calendar_result
- action: tts.speak
target:
entity_id: tts.your_tts_engine
data:
media_player_entity_id: media_player.kitchen_speaker
language: en-US # change language and message text to your own
message: >-
{% set events = calendar_result['calendar.family']['events'] %}
{% set temp = state_attr('weather.forecast_home', 'temperature') %}
{% set waste_days = states('sensor.next_waste_collection') | int(99) %}
{% set price = states('sensor.energi_data_service') | float(0) | round(2) %}
Good morning. It is {{ now().strftime('%A') }}.
The temperature outside is {{ temp }} degrees.
{% if events | length > 0 %}
You have {{ events | length }} appointment(s) today:
{% for event in events %}{{ event.summary }}{% if not loop.last %}, {% endif %}{% endfor %}.
{% else %}Nothing in the calendar today.
{% endif %}
{% if waste_days <= 1 %}The bins are collected today.
{% elif waste_days == 2 %}The bins are collected tomorrow.
{% endif %}
The electricity price is {{ price }} kroner per kilowatt hour. The full story: A Spoken Morning Briefing in Home Assistant: Calendar, Weather, and Electricity Price