Last active
August 23, 2025 18:54
-
-
Save jroehl/e86c6be26fa5d7c81f7db228fb93e960 to your computer and use it in GitHub Desktop.
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: Motion-activated Light (dual time profile + dual wait + lux + sensitivity) | |
| description: > | |
| Turn on lights on motion with two brightness profiles and two wait times | |
| based on a time window (supports overnight). Optional ambient-lux guard, | |
| sensor sensitivity control, transition, and cooldown. | |
| domain: automation | |
| input: | |
| motion_entity: | |
| name: Motion Sensor | |
| selector: | |
| entity: | |
| filter: | |
| domain: binary_sensor | |
| light_target: | |
| name: Light(s) | |
| selector: | |
| target: | |
| entity: | |
| domain: light | |
| # --- Time profiles (single window; choose overnight if crossing midnight) --- | |
| time_window_start: | |
| name: Time window start | |
| selector: { time: {} } | |
| time_window_end: | |
| name: Time window end | |
| selector: { time: {} } | |
| overnight_window: | |
| name: Window crosses midnight | |
| description: If enabled, inside = after start OR before end (e.g., 22:00–06:00). | |
| default: false | |
| selector: { boolean: {} } | |
| # Brightness profiles | |
| brightness_inside_pct: | |
| name: Brightness inside window (%) | |
| default: 60 | |
| selector: | |
| number: { min: 1, max: 100, unit_of_measurement: "%", mode: slider } | |
| brightness_outside_pct: | |
| name: Brightness outside window (%) | |
| default: 30 | |
| selector: | |
| number: { min: 1, max: 100, unit_of_measurement: "%", mode: slider } | |
| # Optional color temperature per profile (0 = skip) | |
| color_temp_inside_kelvin: | |
| name: (Optional) Color temperature inside window (K) | |
| description: Set 0 to skip sending color temperature. | |
| default: 0 | |
| selector: | |
| number: { min: 0, max: 6500, unit_of_measurement: K, mode: slider } | |
| color_temp_outside_kelvin: | |
| name: (Optional) Color temperature outside window (K) | |
| description: Set 0 to skip sending color temperature. | |
| default: 0 | |
| selector: | |
| number: { min: 0, max: 6500, unit_of_measurement: K, mode: slider } | |
| transition_sec: | |
| name: Transition (seconds) | |
| default: 0.5 | |
| selector: | |
| number: { min: 0, max: 10, step: 0.1, unit_of_measurement: s, mode: slider } | |
| # --- Dual wait times --- | |
| no_motion_wait_inside: | |
| name: Wait time inside window (seconds) | |
| description: Time to keep lights on after motion goes off during the inside window. | |
| default: 120 | |
| selector: | |
| number: { min: 0, max: 3600, unit_of_measurement: seconds } | |
| no_motion_wait_outside: | |
| name: Wait time outside window (seconds) | |
| description: Time to keep lights on after motion goes off outside the window. | |
| default: 60 | |
| selector: | |
| number: { min: 0, max: 3600, unit_of_measurement: seconds } | |
| # --- Optional: ambient light guard --- | |
| illuminance_entity: | |
| name: (Optional) Illuminance sensor | |
| default: '' | |
| selector: | |
| entity: | |
| filter: | |
| domain: sensor | |
| illuminance_threshold_lx: | |
| name: Lux threshold (don’t turn on if above) | |
| default: 40 | |
| selector: | |
| number: { min: 1, max: 2000, unit_of_measurement: lx, mode: slider } | |
| # --- Optional: sensor sensitivity control (use whichever your device exposes) --- | |
| set_sensitivity_on_start: | |
| name: Set sensitivity on Home Assistant start | |
| default: false | |
| selector: { boolean: {} } | |
| sensitivity_number: | |
| name: (Optional) Sensitivity as number entity | |
| description: Use if your sensor exposes sensitivity as number.* (e.g., 1–3). | |
| default: '' | |
| selector: | |
| entity: | |
| filter: | |
| domain: number | |
| sensitivity_number_value: | |
| name: Target number value | |
| default: 0 | |
| selector: | |
| number: { min: 0, max: 100, step: 1 } | |
| sensitivity_select: | |
| name: (Optional) Sensitivity as select entity | |
| description: Use if your sensor exposes sensitivity as select.* (e.g., low/med/high). | |
| default: '' | |
| selector: | |
| entity: | |
| filter: | |
| domain: select | |
| sensitivity_select_option: | |
| name: Target select option (exact label) | |
| default: "" | |
| # --- Optional: cooldown after turning off to avoid immediate retrigger --- | |
| cooldown_sec: | |
| name: Cooldown after lights off (ignore motion) | |
| default: 0 | |
| selector: | |
| number: { min: 0, max: 600, unit_of_measurement: s, mode: slider } | |
| mode: restart | |
| max_exceeded: silent | |
| variables: | |
| overnight: !input overnight_window | |
| start_time: !input time_window_start | |
| end_time: !input time_window_end | |
| inside_pct: !input brightness_inside_pct | |
| outside_pct: !input brightness_outside_pct | |
| ct_inside: !input color_temp_inside_kelvin | |
| ct_outside: !input color_temp_outside_kelvin | |
| wait_inside: !input no_motion_wait_inside | |
| wait_outside: !input no_motion_wait_outside | |
| transition: !input transition_sec | |
| lux_entity: !input illuminance_entity | |
| lux_threshold: !input illuminance_threshold_lx | |
| cooldown: !input cooldown_sec | |
| sens_num: !input sensitivity_number | |
| sens_num_value: !input sensitivity_number_value | |
| sens_sel: !input sensitivity_select | |
| sens_sel_option: !input sensitivity_select_option | |
| trigger: | |
| - platform: state | |
| entity_id: !input motion_entity | |
| from: "off" | |
| to: "on" | |
| id: motion_on | |
| - platform: homeassistant | |
| event: start | |
| id: ha_start | |
| condition: [] | |
| action: | |
| - choose: | |
| # --- HA start: optionally set sensitivity once --- | |
| - conditions: | |
| - condition: trigger | |
| id: ha_start | |
| - condition: template | |
| value_template: > | |
| {{ (sens_num != '') or (sens_sel != '' and sens_sel_option != '') }} | |
| - condition: template | |
| value_template: "{{ set_sensitivity_on_start }}" | |
| sequence: | |
| - choose: | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ sens_num != '' }}" | |
| sequence: | |
| - service: number.set_value | |
| target: { entity_id: "{{ sens_num }}" } | |
| data: { value: "{{ sens_num_value }}" } | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ sens_sel != '' and sens_sel_option != '' }}" | |
| sequence: | |
| - service: select.select_option | |
| target: { entity_id: "{{ sens_sel }}" } | |
| data: { option: "{{ sens_sel_option }}" } | |
| # --- Motion ON: evaluate sensitivity, lux, time profile; turn on; then handle off/waits --- | |
| - conditions: | |
| - condition: trigger | |
| id: motion_on | |
| sequence: | |
| # Optionally set sensitivity before acting | |
| - choose: | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ sens_num != '' }}" | |
| sequence: | |
| - service: number.set_value | |
| target: { entity_id: "{{ sens_num }}" } | |
| data: { value: "{{ sens_num_value }}" } | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ sens_sel != '' and sens_sel_option != '' }}" | |
| sequence: | |
| - service: select.select_option | |
| target: { entity_id: "{{ sens_sel }}" } | |
| data: { option: "{{ sens_sel_option }}" } | |
| # Ambient light guard (skip turning on if already bright) | |
| - if: | |
| - condition: template | |
| value_template: > | |
| {% if lux_entity == '' %} | |
| false | |
| {% else %} | |
| {{ (states(lux_entity) | float(0)) > (lux_threshold | float(0)) }} | |
| {% endif %} | |
| then: | |
| - stop: "Ambient light above threshold — skip turning on." | |
| # Compute time window using today_at() (tz-aware, safe) | |
| - variables: | |
| start_dt: "{{ today_at(start_time) }}" | |
| end_dt: "{{ today_at(end_time) }}" | |
| now_dt: "{{ now() }}" | |
| inside_window: > | |
| {% if overnight %} | |
| {{ now_dt >= start_dt or now_dt <= end_dt }} | |
| {% else %} | |
| {{ start_dt <= now_dt <= end_dt }} | |
| {% endif %} | |
| target_brightness: "{{ inside_pct if inside_window else outside_pct }}" | |
| target_wait_sec: "{{ (wait_inside if inside_window else wait_outside) | int(0) }}" | |
| target_ct_int: > | |
| {% set ct = (ct_inside if inside_window else ct_outside) | int(0) %} | |
| {{ ct if ct > 0 else 0 }} | |
| # Turn on with chosen profile | |
| - choose: | |
| # with color temperature (only if > 0) | |
| - conditions: | |
| - condition: template | |
| value_template: "{{ target_ct_int > 0 }}" | |
| sequence: | |
| - service: light.turn_on | |
| target: !input light_target | |
| data: | |
| brightness_pct: "{{ target_brightness }}" | |
| transition: "{{ transition }}" | |
| color_temp_kelvin: "{{ target_ct_int }}" | |
| default: | |
| # without color temperature | |
| - service: light.turn_on | |
| target: !input light_target | |
| data: | |
| brightness_pct: "{{ target_brightness }}" | |
| transition: "{{ transition }}" | |
| # Wait until motion goes OFF, then wait per profile, then turn off | |
| - wait_for_trigger: | |
| - platform: state | |
| entity_id: !input motion_entity | |
| from: "on" | |
| to: "off" | |
| - delay: "{{ target_wait_sec | int }}" | |
| - service: light.turn_off | |
| target: !input light_target | |
| data: | |
| transition: "{{ transition }}" | |
| # Optional cooldown | |
| - if: | |
| - condition: template | |
| value_template: "{{ (cooldown | int(0)) > 0 }}" | |
| then: | |
| - delay: | |
| seconds: "{{ cooldown | int(0) }}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment