blueprint: name: Occupancy Lighting with Manual Override description: > Turns lights on when an occupancy group sensor indicates presence. Turns lights off when the group becomes inactive for the configured delay. A physical switch toggle suppresses sensor-based automation for a configurable period using a timer helper. Create a 'Binary Sensor Group' helper for your room's sensors, and one timer helper per room. domain: automation input: light_target: name: Light selector: target: entity: domain: light occupancy_group: name: Occupancy Sensor / Group description: Select a single binary sensor or a Binary Sensor Group helper containing this room's sensors. selector: entity: domain: binary_sensor switch_input: name: Physical switch input selector: entity: domain: binary_sensor override_timer: name: Override timer selector: entity: domain: timer override_duration: name: Override duration (minutes) default: 30 selector: number: min: 1 max: 1440 step: 1 mode: box unit_of_measurement: minutes off_delay: name: Off delay (seconds) default: 5 selector: number: min: 0 max: 300 step: 1 mode: box unit_of_measurement: seconds mode: restart variables: occupancy_entity: !input occupancy_group timer_entity: !input override_timer duration_minutes: !input override_duration trigger: - id: occupancy_change platform: state entity_id: !input occupancy_group - id: manual_switch platform: state entity_id: !input switch_input - 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: "{{ is_state(occupancy_entity, 'on') }}" - choose: # Case 1: Manual physical switch flipped - conditions: - condition: template value_template: "{{ trigger.id == 'manual_switch' }}" sequence: - action: timer.start target: entity_id: !input override_timer data: # We multiply by 60 because timer.start data maps natively to HH:MM:SS or raw seconds duration: "{{ duration_minutes * 60 }}" # 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 handles turning 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