blueprint: name: Motion based light (With extra spice) description: > Turn a light on based on a motion sensor, unless a blocking entity is active or the illuminance threshold is not met. Optional features: dim lights at night, activate a scene after no-motion timeout, and keep lights on during shower (humidity mode). domain: automation author: CasperBjerggaardJohansen homeassistant: min_version: 2026.3.0 input: motion_sensor: name: Motion Sensor * description: Turns on the light and resets the timeout on each detection. selector: entity: filter: - domain: binary_sensor device_class: - occupancy - motion multiple: false target_light: name: Lights * description: The lights to control. selector: target: entity: - domain: light target_light_brightness: name: Light brightness * description: Brightness when motion is detected (%). default: 100 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: "%" mode: slider target_light_temp: name: Light temperature * description: Color temperature when motion is detected (Kelvin). default: 4200 selector: color_temp: unit: kelvin blocking_entity: name: Blocking entity * description: > When on, prevents the light from turning on and turns it off if it is currently on. selector: entity: filter: - domain: input_boolean multiple: false no_motion_wait: name: Wait time * description: > Seconds to keep the light on after the last motion detection. Resets each time motion is detected. default: 120 selector: number: min: 0 max: 3600 step: 1 unit_of_measurement: seconds mode: slider illuminance: name: Illuminance sensor icon: mdi:sun-wireless description: (optional) input: use_illuminance_sensor: name: Enable illuminance check description: Only turn on the light when illuminance is below the threshold. default: false selector: boolean: illuminance_sensor: name: Illuminance sensor description: Sensor used for the illuminance check. default: {} selector: entity: filter: - domain: sensor multiple: false illuminance_threshold: name: Illuminance threshold description: > Light turns on only when the sensor reads below this value. Ignored if the light is already on. default: 850 selector: number: min: 0 max: 1000 step: 1 mode: slider dim_lights: name: Dim lights at night icon: mdi:lightbulb-on-50 description: (optional) input: night_dim_lights: name: Enable dim lights description: Reduce brightness and/or color temperature between two times. default: false selector: boolean: night_dim_lights_start_time: name: Dimming start time default: "21:00:00" selector: time: night_dim_lights_stop_time: name: Dimming stop time default: "03:00:00" selector: time: night_target_light_brightness: name: Night brightness (%) default: 50 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: "%" mode: slider night_target_light_temp: name: Night color temperature (Kelvin) default: 2700 selector: color_temp: unit: kelvin scene: name: Scene mode icon: mdi:lightbulb-group description: (optional) input: use_scene_bool: name: Enable scene mode description: > Activate a scene instead of turning the light off after the no-motion timeout. The blocking entity still turns the light off. default: false selector: boolean: turn_on_when_scene_starts_bool: name: Activate scene at start time even without motion description: > When enabled, the scene activates at the configured time even if no motion is detected. Blocking entity overrides this. default: false selector: boolean: use_scene_start: name: Scene start time description: Time of day to start using the scene. default: "00:00:00" selector: time: scene_to_use: name: Scene description: Scene to activate after the no-motion timeout. default: {} selector: entity: filter: - domain: scene multiple: false shower_mode: name: Shower mode icon: mdi:shower description: (optional) input: use_humidity_sensor: name: Enable shower mode description: > Keep the light on when humidity is above the threshold, even if motion is not detected. Blocking entity overrides this. default: false selector: boolean: humidity_sensor: name: Humidity sensor default: {} selector: entity: filter: - domain: sensor device_class: humidity multiple: false above_threshold: name: Turn on shower mode above (%) description: Set high enough that only an active shower triggers this. default: 80 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: "%" mode: slider below_threshold: name: Turn off shower mode below (%) description: > Should be 5-10 % below the above threshold. Increase the gap if the light flickers on and off. default: 73 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: "%" mode: slider source_url: https://raw.githubusercontent.com/CasperJohansen/hablueprints/main/motion_light_with_spice.yaml # ───────────────────────────────────────────── mode: restart max_exceeded: silent # ───────────────────────────────────────────── # Trigger-time variables (available in trigger conditions) trigger_variables: use_illuminance: !input use_illuminance_sensor use_scene: !input use_scene_bool turn_on_when_scene_starts: !input turn_on_when_scene_starts_bool use_scene_time: !input use_scene_start # ───────────────────────────────────────────── variables: target_light_brightness: !input target_light_brightness target_light_temp: !input target_light_temp night_dim_lights: !input night_dim_lights night_dim_lights_start_time: !input night_dim_lights_start_time night_dim_lights_stop_time: !input night_dim_lights_stop_time night_target_light_brightness: !input night_target_light_brightness night_target_light_temp: !input night_target_light_temp # NOTE: brightness and temperature are intentionally NOT pre-computed here. # They are evaluated inline in each light.turn_on call so that now() is # called at the moment the light actually turns on, not at trigger time. # This ensures the correct day/night values are always used. shower_mode: !input use_humidity_sensor shower_humidity_sensor: !input humidity_sensor shower_above_threshold: !input above_threshold shower_keep_on: > {{ shower_mode == true and shower_humidity_sensor not in ('', {}, none) and states(shower_humidity_sensor) | int(0) > shower_above_threshold }} # ───────────────────────────────────────────── trigger: - platform: state entity_id: !input motion_sensor to: "on" - platform: state entity_id: !input blocking_entity to: "on" - platform: time at: !input use_scene_start id: scene-on # ───────────────────────────────────────────── action: # ── SCENE-ON TRIGGER ────────────────────── # Only fires when motion is clear. If motion is active, the running # motion instance handles the scene after the timeout itself. - if: - condition: trigger id: scene-on then: - if: - condition: state entity_id: !input blocking_entity state: "off" - condition: state entity_id: !input motion_sensor state: "off" then: - if: - condition: template value_template: "{{ turn_on_when_scene_starts == true }}" then: - action: scene.turn_on target: entity_id: !input scene_to_use # ── MOTION / BLOCKING TRIGGER ───────────── - if: - condition: not conditions: - condition: trigger id: scene-on then: - if: # Motion detected and not blocked → turn lights on - condition: state entity_id: !input motion_sensor state: "on" - condition: state entity_id: !input blocking_entity state: "off" then: - if: - condition: template value_template: "{{ use_illuminance == true }}" then: # Only turn on if dark enough, or the light is already on - if: - condition: or conditions: - condition: numeric_state entity_id: !input illuminance_sensor below: !input illuminance_threshold - condition: template value_template: > {{ expand(area_entities( area_id(target_entity_id | first))) | selectattr('domain', 'eq', 'light') | selectattr('state', 'eq', 'on') | list | count > 0 }} then: - action: light.turn_on target: !input target_light data: brightness_pct: > {% set t = now().strftime('%H:%M:%S') %} {% if night_dim_lights == true and (t >= night_dim_lights_start_time or t < night_dim_lights_stop_time) %} {{ night_target_light_brightness }} {% else %} {{ target_light_brightness }} {% endif %} color_temp_kelvin: > {% set t = now().strftime('%H:%M:%S') %} {% if night_dim_lights == true and (t >= night_dim_lights_start_time or t < night_dim_lights_stop_time) %} {{ night_target_light_temp | int }} {% else %} {{ target_light_temp | int }} {% endif %} - wait_for_trigger: - platform: state entity_id: !input motion_sensor from: "on" to: "off" - delay: !input no_motion_wait - if: - condition: template value_template: > {{ now().strftime('%H:%M:%S') >= use_scene_time and use_scene == true }} then: - action: scene.turn_on target: entity_id: !input scene_to_use else: - if: - condition: template value_template: "{{ shower_keep_on == true }}" then: - wait_for_trigger: - platform: numeric_state entity_id: !input humidity_sensor below: !input below_threshold - action: light.turn_off target: !input target_light else: - action: light.turn_off target: !input target_light else: # Illuminance too high and light is off → ensure light is off - if: - condition: template value_template: "{{ shower_keep_on == true }}" then: - wait_for_trigger: - platform: numeric_state entity_id: !input humidity_sensor below: !input below_threshold - action: light.turn_off target: !input target_light else: - action: light.turn_off target: !input target_light else: # No illuminance check → turn on directly - action: light.turn_on target: !input target_light data: brightness_pct: > {% set t = now().strftime('%H:%M:%S') %} {% if night_dim_lights == true and (t >= night_dim_lights_start_time or t < night_dim_lights_stop_time) %} {{ night_target_light_brightness }} {% else %} {{ target_light_brightness }} {% endif %} color_temp_kelvin: > {% set t = now().strftime('%H:%M:%S') %} {% if night_dim_lights == true and (t >= night_dim_lights_start_time or t < night_dim_lights_stop_time) %} {{ night_target_light_temp | int }} {% else %} {{ target_light_temp | int }} {% endif %} - wait_for_trigger: - platform: state entity_id: !input motion_sensor from: "on" to: "off" - delay: !input no_motion_wait - if: - condition: template value_template: > {{ now().strftime('%H:%M:%S') >= use_scene_time and use_scene == true }} then: - action: scene.turn_on target: entity_id: !input scene_to_use else: - if: - condition: template value_template: "{{ shower_keep_on == true }}" then: - wait_for_trigger: - platform: numeric_state entity_id: !input humidity_sensor below: !input below_threshold - action: light.turn_off target: !input target_light else: - action: light.turn_off target: !input target_light else: # Blocking entity turned on → force light off. # Shower mode cannot override the blocking entity. - action: light.turn_off target: !input target_light