Skip to content

Instantly share code, notes, and snippets.

@jamesharding
Last active February 16, 2026 17:44
Show Gist options
  • Select an option

  • Save jamesharding/5d994f7c2abe23bd8f28853258b37d27 to your computer and use it in GitHub Desktop.

Select an option

Save jamesharding/5d994f7c2abe23bd8f28853258b37d27 to your computer and use it in GitHub Desktop.
Presence automation blueprint for Home Assistant, using binary sensors, media players, etc.
blueprint:
name: Presence Automations
description: >
Presence-based automation system with day/sunset/night lighting scenes, binary
device control, custom script triggers, configurable timeouts, manual override
detection, and occupancy tracking.
Supports motion sensors (bursty), media players, door sensors, and static presence indicators.
Uses rising-edge detection: lights, devices, and scripts only activate at the moment
the room transitions from unoccupied to occupied. While the room remains occupied,
no adjustments are made — if you manually turn off lights, they stay off until you
leave and return. Requires an Occupied Helper (input_boolean) per room.
Time transition support: if the room is occupied with ALL lights off and the time
period changes (e.g., day→sunset, sunset→night), lights will automatically turn on
using the new period's scene/brightness — provided auto-on is enabled for that period.
Lights that are already on will not be changed during a time transition.
On the falling edge (room empties after timeout), lights and devices turn off and
on_no_presence scripts are executed.
Each period (day/sunset/night) has two independent controls:
- Timeout: How long to wait after presence clears before turning off.
- Auto-On: Controls whether lights turn on automatically when presence is detected.
All automated actions (lights, devices, scripts) can be disabled via the
Automations Toggle without affecting occupancy tracking.
domain: automation
author: James Harding
input:
# ===== PRESENCE SENSORS =====
presence_binary_sensors:
name: Binary Presence Sensors
description: >
Motion sensors, door sensors, or any binary_sensor that indicates presence when "on".
selector:
entity:
domain: binary_sensor
multiple: true
default: []
presence_media_players:
name: Media Players
description: >
Media players that indicate presence when actively playing.
Room is considered occupied only when state is "playing".
selector:
entity:
domain: media_player
multiple: true
default: []
# ===== LIGHTS =====
lights:
name: Lights
description: The lights to control in this room.
selector:
entity:
domain: light
multiple: true
# ===== DEVICES =====
binary_devices:
name: Binary Devices (Optional)
description: >
Switches, smart plugs, fans, or other on/off devices to turn on/off
based on presence. Turned on when the room becomes occupied (rising edge)
and off when the room empties (falling edge).
selector:
entity:
domain:
- switch
- fan
- input_boolean
multiple: true
default: []
# ===== SCRIPTS =====
on_presence_scripts:
name: On-Presence Scripts (Optional)
description: >
Scripts to run when the room first becomes occupied (rising edge).
Only triggered once per occupancy cycle, not on repeated sensor triggers.
selector:
entity:
domain: script
multiple: true
default: []
on_no_presence_scripts:
name: On-No-Presence Scripts (Optional)
description: >
Scripts to run when the room empties (falling edge, after timeout).
Useful for cleanup actions, setting modes, etc.
selector:
entity:
domain: script
multiple: true
default: []
# ===== SCENES =====
sunset_scene:
name: Sunset Scene
description: >
Scene to activate from sunset until night mode begins.
If not set, lights will turn on at 80% brightness.
selector:
entity:
domain: scene
default: ""
night_scene:
name: Night Scene
description: >
Scene to activate during night mode (e.g., late evening/sleep time).
If not set, lights will turn on at 5% brightness.
selector:
entity:
domain: scene
default: ""
day_scene:
name: Day Scene (Optional)
description: >
Scene to activate during daytime (before sunset).
If not set, lights will turn on at 100% brightness.
Only used when daytime auto-on is enabled.
selector:
entity:
domain: scene
default: ""
day_auto_on:
name: Daytime Auto Turn-On
description: >
When enabled, lights automatically turn on during daytime when presence
is detected (using the Day Scene or 100% brightness).
When disabled, lights will NOT turn on automatically, but will still
auto-turn-off when the room empties (if Daytime Timeout > 0).
default: false
selector:
boolean:
sunset_auto_on:
name: Sunset Auto Turn-On
description: >
When enabled, lights automatically turn on after sunset when presence
is detected (using the Sunset Scene or 80% brightness).
When disabled, lights will NOT turn on automatically, but will still
auto-turn-off when the room empties (if Sunset Timeout > 0).
default: true
selector:
boolean:
night_auto_on:
name: Night Auto Turn-On
description: >
When enabled, lights automatically turn on during night mode when presence
is detected (using the Night Scene or 5% brightness).
When disabled, lights will NOT turn on automatically, but will still
auto-turn-off when the room empties (if Night Timeout > 0).
default: true
selector:
boolean:
# ===== HELPERS =====
occupied_helper:
name: Occupied Helper
description: >
An input_boolean to track room occupancy. Required for rising-edge detection —
ensures lights/devices are only turned on at the exact moment the room becomes
occupied and not on subsequent sensor re-triggers. Create one input_boolean per
room (e.g., input_boolean.office_occupied).
selector:
entity:
domain: input_boolean
automations_toggle:
name: Automations Toggle (Optional)
description: >
An input_boolean that enables/disables all automated actions (lights,
devices, and scripts). When "off", nothing turns on/off automatically,
but occupancy tracking still works.
selector:
entity:
domain: input_boolean
default: ""
# ===== TIMEOUTS =====
timeout_day:
name: Daytime Timeout (minutes)
description: >
How long to wait after presence clears before turning off lights (daytime).
Auto-turn-on is controlled separately by the "Daytime Auto Turn-On" toggle.
default: 10
selector:
number:
min: 1
max: 60
unit_of_measurement: minutes
mode: slider
timeout_sunset:
name: Sunset Timeout (minutes)
description: >
How long to wait after presence clears before turning off lights (sunset period).
Auto-turn-on is controlled separately by the "Sunset Auto Turn-On" toggle.
default: 10
selector:
number:
min: 1
max: 60
unit_of_measurement: minutes
mode: slider
timeout_night:
name: Night Timeout (minutes)
description: >
How long to wait after presence clears before turning off lights (night period).
Auto-turn-on is controlled separately by the "Night Auto Turn-On" toggle.
default: 5
selector:
number:
min: 1
max: 60
unit_of_measurement: minutes
mode: slider
# ===== TIME SENSORS =====
night_mode_sensor:
name: Night Mode Sensor
description: >
A binary_sensor that is "on" during night mode (e.g., late evening/sleep time).
selector:
entity:
domain: binary_sensor
default: binary_sensor.night
sunset_sensor:
name: Sunset Sensor
description: >
A binary_sensor that is "on" after sunset (typically sun.sun based).
selector:
entity:
domain: binary_sensor
default: binary_sensor.sunset
# ===== TRIGGERS =====
# Trigger on any state change of presence entities (not just to specific states)
# This handles bursty sensors better and catches media player state changes
trigger:
- platform: state
entity_id: !input presence_binary_sensors
id: binary_sensor_change
- platform: state
entity_id: !input presence_media_players
id: media_player_change
# Trigger on time period transitions (sunset/night sensors changing state)
# so lights can turn on when the room is already occupied with lights off
- platform: state
entity_id: !input sunset_sensor
id: time_transition
- platform: state
entity_id: !input night_mode_sensor
id: time_transition
# ===== VARIABLES =====
# These are evaluated at automation start and can be re-evaluated in templates
variables:
# Input variables
presence_binary_sensors: !input presence_binary_sensors
presence_media_players: !input presence_media_players
lights: !input lights
binary_devices: !input binary_devices
on_presence_scripts: !input on_presence_scripts
on_no_presence_scripts: !input on_no_presence_scripts
sunset_scene: !input sunset_scene
night_scene: !input night_scene
day_scene: !input day_scene
day_auto_on: !input day_auto_on
sunset_auto_on: !input sunset_auto_on
night_auto_on: !input night_auto_on
occupied_helper: !input occupied_helper
automations_toggle: !input automations_toggle
timeout_day: !input timeout_day
timeout_sunset: !input timeout_sunset
timeout_night: !input timeout_night
night_mode_sensor: !input night_mode_sensor
sunset_sensor: !input sunset_sensor
# Default brightness percentages (when no scene is configured)
brightness_day: 100
brightness_sunset: 80
brightness_night: 5
# Transition times (in seconds)
transition_day: 1
transition_sunset: 2
transition_night: 3
# ===== CONDITIONS =====
condition: []
# ===== ACTIONS =====
action:
# Calculate current presence state (evaluated fresh each time)
- variables:
# Helper template to check binary sensors
check_binary_presence: >
{{ presence_binary_sensors | default([]) | select('is_state', 'on') | list | length > 0 }}
# Helper template to check media players
check_media_presence: >
{{ presence_media_players | default([]) | select('is_state', 'playing') | list | length > 0 }}
# Combined presence check
presence_active: >
{{ check_binary_presence or check_media_presence }}
automation_enabled: >
{{ automations_toggle == '' or automations_toggle is none or is_state(automations_toggle, 'on') }}
# Time period detection:
# day = sun has not set
# sunset = sun has set, but night mode not active
# night = sun has set AND night mode active
is_day: >
{{ not is_state(sunset_sensor, 'on') }}
is_sunset: >
{{ is_state(sunset_sensor, 'on') and not is_state(night_mode_sensor, 'on') }}
is_night: >
{{ is_state(sunset_sensor, 'on') and is_state(night_mode_sensor, 'on') }}
has_devices: >
{{ binary_devices | default([]) | length > 0 }}
has_on_presence_scripts: >
{{ on_presence_scripts | default([]) | length > 0 }}
has_on_no_presence_scripts: >
{{ on_no_presence_scripts | default([]) | length > 0 }}
# Rising-edge detection: was the room already occupied before this trigger?
was_already_occupied: >
{{ is_state(occupied_helper, 'on') }}
timeout_minutes: >
{% if is_night %}{{ timeout_night }}
{% elif is_sunset %}{{ timeout_sunset }}
{% else %}{{ timeout_day }}
{% endif %}
turn_on_enabled: >
{% if is_night %}{{ night_auto_on }}
{% elif is_sunset %}{{ sunset_auto_on }}
{% else %}{{ day_auto_on }}
{% endif %}
all_lights_off: >
{{ lights | select('is_state', 'on') | list | length == 0 }}
# Resolved scene/brightness/transition for the current time period
target_scene: >
{% if is_night %}{{ night_scene }}
{% elif is_sunset %}{{ sunset_scene }}
{% else %}{{ day_scene }}
{% endif %}
target_brightness: >
{% if is_night %}{{ brightness_night }}
{% elif is_sunset %}{{ brightness_sunset }}
{% else %}{{ brightness_day }}
{% endif %}
target_transition: >
{% if is_night %}{{ transition_night }}
{% elif is_sunset %}{{ transition_sunset }}
{% else %}{{ transition_day }}
{% endif %}
- choose:
# =====================
# TIME PERIOD TRANSITION (while occupied, all lights off)
# =====================
# When the time period changes (day→sunset, sunset→night, etc.) and the
# room is already occupied with ALL lights off, turn on lights for the
# new period. Does not change lights that are already on.
- conditions:
- condition: trigger
id: time_transition
- condition: template
value_template: "{{ was_already_occupied }}"
- condition: template
value_template: "{{ presence_active }}"
- condition: template
value_template: "{{ all_lights_off }}"
- condition: template
value_template: "{{ turn_on_enabled }}"
- condition: template
value_template: "{{ automation_enabled }}"
sequence:
# Turn on lights using the resolved scene or brightness for the current period
- if:
- condition: template
value_template: "{{ target_scene != '' and target_scene is not none }}"
then:
- service: scene.turn_on
target:
entity_id: "{{ target_scene }}"
else:
- service: light.turn_on
target:
entity_id: "{{ lights }}"
data:
brightness_pct: "{{ target_brightness }}"
transition: "{{ target_transition }}"
# =====================
# PRESENCE DETECTED (rising edge)
# =====================
- conditions:
- condition: template
value_template: "{{ presence_active }}"
sequence:
# Update occupied helper
- service: input_boolean.turn_on
target:
entity_id: "{{ occupied_helper }}"
# Rising edge: only act on the transition from unoccupied → occupied
- condition: template
value_template: "{{ not was_already_occupied }}"
# All automated actions gated by automations toggle
- condition: template
value_template: "{{ automation_enabled }}"
# Turn on binary devices
- if:
- condition: template
value_template: "{{ has_devices }}"
then:
- service: homeassistant.turn_on
target:
entity_id: "{{ binary_devices }}"
# Run on-presence scripts
- if:
- condition: template
value_template: "{{ has_on_presence_scripts }}"
then:
- service: script.turn_on
target:
entity_id: "{{ on_presence_scripts }}"
# Only turn on lights if auto-on is enabled for this period
- condition: template
value_template: "{{ turn_on_enabled }}"
# Only turn on lights if ALL lights are currently off
# (don't override manually controlled lights)
- condition: template
value_template: "{{ all_lights_off }}"
# Turn on lights using the resolved scene or brightness for the current period
- if:
- condition: template
value_template: "{{ target_scene != '' and target_scene is not none }}"
then:
- service: scene.turn_on
target:
entity_id: "{{ target_scene }}"
else:
- service: light.turn_on
target:
entity_id: "{{ lights }}"
data:
brightness_pct: "{{ target_brightness }}"
transition: "{{ target_transition }}"
# =====================
# PRESENCE CLEARED (falling edge)
# =====================
- conditions:
- condition: template
value_template: "{{ not presence_active }}"
sequence:
# Wait for timeout before taking action
# Note: mode: restart ensures if presence is detected during delay, automation restarts
- delay:
minutes: "{{ timeout_minutes | int }}"
# All automated actions gated by automations toggle
- if:
- condition: template
value_template: "{{ automation_enabled }}"
then:
# Pre-off warning flash (only if lights are currently on)
- if:
- condition: template
value_template: "{{ lights | select('is_state', 'on') | list | length > 0 }}"
then:
# Flash sequence: gentle brightness pulse
- service: light.turn_on
target:
entity_id: "{{ lights }}"
data:
brightness_step_pct: 15
transition: 0.3
- delay:
milliseconds: 400
- service: light.turn_on
target:
entity_id: "{{ lights }}"
data:
brightness_step_pct: -15
transition: 0.3
- delay:
milliseconds: 600
# Wait 30 seconds after flash before turning off
- delay:
seconds: 30
# Turn off lights with transition
- service: light.turn_off
target:
entity_id: "{{ lights }}"
data:
transition: "{{ target_transition }}"
# Turn off binary devices
- if:
- condition: template
value_template: "{{ has_devices }}"
then:
- service: homeassistant.turn_off
target:
entity_id: "{{ binary_devices }}"
# Run on-no-presence scripts
- if:
- condition: template
value_template: "{{ has_on_no_presence_scripts }}"
then:
- service: script.turn_on
target:
entity_id: "{{ on_no_presence_scripts }}"
# Update occupied helper
- service: input_boolean.turn_off
target:
entity_id: "{{ occupied_helper }}"
# Restart mode ensures bursty sensors work correctly:
# - New motion detection restarts the automation, preventing premature turn-off
# - Handles rapid on/off transitions gracefully
mode: restart
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment