From edc7fe4f505f229072e16d6615567f9081961afe Mon Sep 17 00:00:00 2001 From: Bogdan Bagno Date: Sat, 20 Jun 2026 00:09:02 +0100 Subject: [PATCH] Add initial Occupancy Lighting with Manual Override blueprint --- Occupancy_Lighting_with_Manual_Override.yaml | 169 +++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 Occupancy_Lighting_with_Manual_Override.yaml diff --git a/Occupancy_Lighting_with_Manual_Override.yaml b/Occupancy_Lighting_with_Manual_Override.yaml new file mode 100644 index 0000000..f24a23c --- /dev/null +++ b/Occupancy_Lighting_with_Manual_Override.yaml @@ -0,0 +1,169 @@ +blueprint: + name: Occupancy Lighting with Manual Override + description: > + Turns lights on when any presence sensor, motion sensor, or door sensor + indicates occupancy. Turns lights off when all sensors are inactive for + the configured delay. + + A physical switch input (e.g. Shelly in detached mode) toggles the light + and suppresses sensor-based automation for a configurable period using a + timer helper. + + Create one timer helper per room and pass it to the blueprint. + domain: automation + input: + light_target: + name: Light + selector: + target: + entity: + filter: + domain: light + + presence_sensors: + name: Presence sensors + default: [] + selector: + entity: + filter: + domain: binary_sensor + multiple: true + + motion_sensors: + name: Motion sensors + default: [] + selector: + entity: + filter: + domain: binary_sensor + multiple: true + + door_sensors: + name: Door sensors + default: [] + selector: + entity: + filter: + domain: binary_sensor + multiple: true + + switch_input: + name: Physical switch input + selector: + entity: + filter: + domain: binary_sensor + + override_timer: + name: Override timer + selector: + entity: + filter: + domain: timer + + override_duration: + name: Override duration (seconds) + default: 1800 + selector: + number: + min: 60 + max: 86400 + step: 60 + mode: box + unit_of_measurement: seconds + + off_delay: + name: Off delay (seconds) + default: 5 + selector: + number: + min: 0 + max: 300 + step: 1 + mode: box + unit_of_measurement: seconds + +mode: restart + +# Define variables globally so they evaluate properly before actions run +variables: + presence_entities: !input presence_sensors + motion_entities: !input motion_sensors + door_entities: !input door_sensors + timer_entity: !input override_timer + +trigger: + - id: occupancy_change + platform: state + entity_id: !input presence_sensors + + - id: occupancy_change + platform: state + entity_id: !input motion_sensors + + - id: occupancy_change + platform: state + entity_id: !input door_sensors + + - id: manual_switch + platform: state + entity_id: !input switch_input + from: "off" + to: "on" + + - id: timer_finished + platform: event + event_type: timer.finished + event_data: + entity_id: !input override_timer + +action: + - variables: + override_active: "{{ is_state(timer_entity, 'active') }}" + occupied: > + {{ + (presence_entities | length > 0 and expand(presence_entities) | selectattr('state', 'eq', 'on') | list | count > 0) or + (motion_entities | length > 0 and expand(motion_entities) | selectattr('state', 'eq', 'on') | list | count > 0) or + (door_entities | length > 0 and expand(door_entities) | selectattr('state', 'eq', 'on') | list | count > 0) + }} + + - choose: + # Case 1: Manual physical switch toggled + - conditions: + - condition: template + value_template: "{{ trigger.id == 'manual_switch' }}" + sequence: + - action: light.toggle + target: !input light_target + - action: timer.start + target: + entity_id: !input override_timer + data: + duration: !input override_duration + + # Case 2: Occupancy changed, but manual override is currently active (Do Nothing) + - conditions: + - condition: template + value_template: "{{ trigger.id == 'occupancy_change' and override_active }}" + sequence: [] + + # Case 3: Occupancy detected and override is NOT active + - conditions: + - condition: template + value_template: "{{ occupied }}" + sequence: + - action: light.turn_on + target: !input light_target + + # Default Case: No occupancy detected / timer finished -> sequence turns light off + default: + - delay: + seconds: !input off_delay + - condition: template + value_template: > + {{ + not occupied and not is_state(timer_entity, 'active') + }} + - action: light.turn_off + target: !input light_target +