Last active
February 16, 2026 17:44
-
-
Save jamesharding/5d994f7c2abe23bd8f28853258b37d27 to your computer and use it in GitHub Desktop.
Presence automation blueprint for Home Assistant, using binary sensors, media players, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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