blueprint: name: "Custom: Motion Light" description: > Smart motion sensor automation blueprint. Note: by default will not run if light was already ON. If light was turned ON during automation running the automation will enter manual state and will not triiger untill the light will be turned off. Note: - Not tested when motion sensors and state sensors are used at the same time. domain: automation input: controls: name: "Controls" collapsed: false input: motion_sensors: name: Motion sensors description: Select one or more motion sensors. Light is ON if any of the objects is ON. default: [] selector: entity: domain: - binary_sensor - switch - group - light - binary_sensor multiple: true condition_switches: name: Condition switches description: > Automation will not trigger if any of switches is off default: [] selector: entity: domain: - input_boolean - switch - group - light - binary_sensor multiple: true devices_group: name: "Devices" collapsed: false input: target_light: name: Target Light (optional) description: "Light to control. Setup no light or single light." default: [] selector: entity: domain: light multiple: true target_light_data: name: Light Data Dictionary (optional) default: "" description: > Provide a YAML dictionary of light.turn_on parameters. If parameter not specified then last set if taken. Example: brightness: 200 color_temp: 350 rgb_color: [255, 0, 0] effect: rainbow selector: object: {} brightness_threshold: name: Brightness Threshold description: 'Will trigger automation only if brightness of enabled light is lower then the threshold value' default: 0 selector: number: min: 0 max: 255 step: 1 target_switch: name: Target Switch (optional) description: "Switch to control. Setup no switch or single switch." default: [] selector: entity: domain: switch multiple: true timeout_delay: name: Timeout delay (seconds) description: Optional delay for motion sensors before turning off the light after all motion sensors are OFF default: 0 selector: number: min: 0 max: 3600 step: 1 unit_of_measurement: seconds persistent_state: name: "Persistent State" collapsed: true input: automation_state_entity: name: Automation state entity description: "`input_text` that stores the light automation state in JSON format. `Doesn't require specific initial state, values of the entity can be empty. For now each automation must have it's personal entity.`" selector: entity: domain: input_text automation_state_placeholder_key: name: Automation state placeholder key description: Overrides key for persistent storage if not empty. By default uses identifier of target light, otherwise uses constant. `Don't override it if you don't understand the meaning` default: '' selector: text: luminance: name: "Luminance" collapsed: true input: luminance_sensor: name: Luminance sensor (optional) description: Sensor reporting ambient light level (lux) default: null selector: entity: domain: sensor luminance_threshold: name: Luminance threshold (optional) description: Light will only turn on if sensor value is below this threshold default: 100 selector: number: min: 0 max: 10000 step: 1 unit_of_measurement: "lux" luminance_enable_switch: name: Luminance control enable switch (optional) description: Switch or input_boolean to enable/disable luminance control default: null selector: entity: domain: - switch - input_boolean actions: name: "Actions" collapsed: true input: user_condition: name: Condition block description: Optional condition(s) that must pass for actions to run default: [] selector: condition: {} enable_action: name: Enable callback action (optional) description: Runs when light is turned on default: [] selector: action: {} disable_action: name: Disable callback action (optional) description: Runs when light is turned off default: [] selector: action: {} manual_action_runs_disable_action: name: Manual also runs disable action description: > If checked, executing `Manual Action` will combine it with `Disable Action`. default: false selector: boolean: {} manual_action: name: Manual callback action (optional) description: > Runs when light state is changed during automation running. Works only in case if `Automation state entity` is set. default: [] selector: action: {} mode: restart trigger: # Motion sensors ON/OFF - platform: state entity_id: !input motion_sensors id: "motion_sensor" # Condition switches ON/OFF - platform: state entity_id: !input condition_switches # Light ON/OFF - platform: state entity_id: !input target_light id: "light_state_changed" # Switches ON/OFF - platform: state entity_id: !input target_switch id: "switch_state_changed" # Luminance sensor ON/OFF - platform: template value_template: > {% if luminance_sensor %} {{ states(luminance_sensor) not in ['unknown','unavailable'] }} {% else %} false {% endif %} - platform: template value_template: > {% if luminance_enable_switch %} {{ states(luminance_enable_switch) not in ['unknown','unavailable'] }} {% else %} false {% endif %} condition: !input user_condition # TOFIX: # - state_sensors # - might be problems with storing persistent state variables: # Constants is_debug: false is_base_debug: false # JSON state constants automation_state_invalid: '-1' automation_state_none: '0' automation_state_enabled: '1' automation_state_enabling: '2' automation_state_manual: '3' state_motion_light_state: 'mls' state_motion_light_last_action_timestamp: 'mllat' state_motion_light_last_brightness: 'mllb' date_time_now: "{{ now() }}" trigger_id: "{{ trigger.id }}" # Defines sensors: !input motion_sensors condition_switches: !input condition_switches timeout: !input timeout_delay brightness_threshold: !input brightness_threshold # Light light_entities: !input target_light light_entity: "{{ light_entities[0] if light_entities | length != 0 else none }}" # Switch switch_entities: !input target_switch switch_entity: "{{ switch_entities[0] if switch_entities | length != 0 else none }}" # JSON global state. automation_state_entity: !input automation_state_entity automation_state_global: > {% set text = states(automation_state_entity) | string %} {% if text in ['unknown','unavailable','none',''] %} {{ dict() }} {% else %} {{ text | from_json }} {% endif %} automation_state_placeholder_key: !input automation_state_placeholder_key automation_state_key: > {% if automation_state_placeholder_key != '' %} {{ automation_state_placeholder_key }} {% elif switch_entity is not none %} {{ switch_entity }} {% elif light_entity is not none %} {{ light_entity }} {% else %} 'default_motion_light_placeholder' {% endif %} automation_state: "{{ automation_state_global.get(automation_state_key, dict()) if light_entity != '' else dict() }}" motion_light_state: "{{ automation_state.get(state_motion_light_state, automation_state_none) }}" motion_light_last_action_timestamp: > {% if trigger_id == 'state_motion' %} {{ date_time_now }} {% else %} {{ (automation_state.get(state_motion_light_last_action_timestamp, none)) }} {% endif %} state_is_none: "{{ ((motion_light_state | string) == automation_state_none) }}" state_is_enabled: "{{ (motion_light_state | string) == automation_state_enabled }}" state_is_enabling: "{{ (motion_light_state | string) == automation_state_enabling }}" state_is_manual: "{{ (motion_light_state | string) == automation_state_manual }}" # Actions manual_action: !input manual_action disable_action: !input disable_action enable_action: !input enable_action manual_action_runs_disable_action: !input manual_action_runs_disable_action light_data: !input target_light_data # Luminance luminance_sensor: !input luminance_sensor luminance_threshold: !input luminance_threshold luminance_enable_switch: !input luminance_enable_switch luminance_ok: > {% if luminance_sensor is not none and luminance_threshold is not none %} {% set val = states(luminance_sensor) | float(0) %} {% set enabled = true %} {% if luminance_enable_switch %} {% set enabled = is_state(luminance_enable_switch, 'on') %} {% endif %} {{ enabled and val < luminance_threshold }} {% else %} true {% endif %} # Trigger details all_of_condition_switches_on: > {% set e = condition_switches if condition_switches is iterable else [condition_switches] %} {{ (e | select('is_state', 'on') | list | length) == condition_switches | length }} count_of_enabled_sensor: > {% set e = sensors if sensors is iterable else [sensors] %} {{ e | select('is_state', 'on') | list | length }} motion_on: "{{ count_of_enabled_sensor > 0 }}" motion_all_off: "{{ count_of_enabled_sensor == 0 }}" must_be_enabled_preview: > {{ (all_of_condition_switches_on and luminance_ok and motion_on) | bool }} must_be_enabled_guard: "{{ state_is_none }}" must_be_enabled: > {{ must_be_enabled_preview and must_be_enabled_guard }} must_be_disabled_preview: > {{ ((not all_of_condition_switches_on) or motion_all_off) | bool }} must_be_disabled_guard: "{{ state_is_enabled }}" must_be_disabled: > {{ must_be_disabled_preview and must_be_disabled_guard }} action: # Debug info. - choose: - conditions: - condition: template value_template: "{{ is_base_debug }}" sequence: - service: persistent_notification.create data: title: "Debug Info" message: > must_be_enabled_preview: {{ must_be_enabled_preview }}, must_be_disabled_preview: {{ must_be_disabled_preview }}, must_be_disabled: {{ must_be_disabled }}, must_be_disabled_guard: {{ must_be_disabled_guard }}, id: {{ trigger.id }} # Guard for 1 light. - choose: conditions: - condition: template value_template: "{{ light_entities | length > 1}}" sequence: stop: "Only one light is supported currently" # Guard for 1 switch. - choose: conditions: - condition: template value_template: "{{ switch_entities | length > 1}}" sequence: stop: "Only one switch is supported currently" - choose: # Disable automation flag if light was changed during automation - conditions: - condition: template value_template: "{{ trigger_id == 'light_state_changed' or trigger_id == 'switch_state_changed' }}" sequence: - choose: # Disable state if light was turned OFF (no matter how) - conditions: - condition: template value_template: > {% set res = false %} {% if light_entity is not none %} {% set brightness = state_attr(light_entity, 'brightness') %} {% set res = res and (is_state(light_entity, 'off') or brightness | int < brightness_threshold) %} {% endif %} {% if switch_entity is not none %} {% set res = res and is_state(switch_entity, 'off') %} {% endif %} {{ res }} sequence: - service: input_text.set_value target: entity_id: "{{ automation_state_entity }}" data: value: > {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_none })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} # This call goes DIRECTLY from the automation - conditions: - condition: template value_template: "{{ state_is_enabling }}" sequence: - service: input_text.set_value target: entity_id: "{{ automation_state_entity }}" data: value: > {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_enabled })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} # If the control was taken by the user - conditions: - condition: template value_template: "{{ state_is_enabled }}" sequence: - service: input_text.set_value target: entity_id: "{{ automation_state_entity }}" data: > {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_manual })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} # Call disable action if required - choose: - conditions: "{{ manual_action_runs_disable_action and disable_action != [] }}" sequence: !input disable_action # Call manual action - choose: - conditions: "{{ manual_action != [] }}" sequence: !input manual_action # Enable path - conditions: - condition: template value_template: "{{ must_be_enabled }}" sequence: - choose: # Guard: stop if light already ON and automation_flag exists - conditions: - condition: template value_template: > {% set res = false %} {% if light_entity is not none %} {% set res = res or ((is_state(light_entity, 'on') or state_attr(light_entity, 'brightness') | int > brightness_threshold)) %} {% endif %} {% if switch_entity is not none %} {% set res = res or is_state(switch_entity, 'on') %} {% endif %} {{ res }} sequence: - stop: "Light is already ON when sensors were triggered" # Enable the light default: # Debug info. - choose: - conditions: - condition: template value_template: "{{ is_debug }}" sequence: - service: persistent_notification.create data: title: "Debug Info (Must Be Enabled)" message: > Enabled. light_entity: {{ light_entity }} - variables: last_brightness: > {% if (light_entity is none) or is_state(light_entity, 'off') %} 0 {% else %} {{ state_attr(light_entity, 'brightness') }} {% endif %} # Turn ON the light - choose: - conditions: - condition: template value_template: "{{ light_entity is not none }}" sequence: - service: light.turn_on target: entity_id: "{{ light_entity }}" data: "{{ light_data }}" # Enable the switch. - choose: - conditions: - condition: template value_template: "{{ switch_entity is not none }}" sequence: - service: switch.turn_on target: entity_id: "{{ switch_entity }}" - service: input_text.set_value target: entity_id: "{{ automation_state_entity }}" data: value: > {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_enabling, state_motion_light_last_action_timestamp: date_time_now, state_motion_light_last_brightness: last_brightness })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} # Enable action - choose: - conditions: - condition: template value_template: "{{ enable_action != [] }}" sequence: !input enable_action # Disable path - conditions: - condition: template value_template: "{{ must_be_disabled }}" sequence: # Debug info. - choose: - conditions: - condition: template value_template: "{{ is_debug }}" sequence: - service: persistent_notification.create data: title: "Debug Info (Must Be Disabled)" message: > Disabled. light_entity: {{ light_entity }} - delay: seconds: "{{ timeout }}" # Disable the light. - choose: - conditions: - condition: template value_template: "{{ light_entity is not none }}" sequence: - variables: last_brightness: "{{ automation_state.get(state_motion_light_last_brightness, 0) }}" - choose: - conditions: - condition: template value_template: "{{ last_brightness > 0 }}" sequence: - service: light.turn_on target: entity_id: "{{ light_entity }}" data: brightness: "{{ last_brightness }}" default: - service: light.turn_off target: entity_id: "{{ light_entity }}" # Disable the switch. - choose: - conditions: - condition: template value_template: "{{ switch_entity is not none }}" sequence: - service: switch.turn_off target: entity_id: "{{ switch_entity }}" # Modify automation entity. - service: input_text.set_value target: entity_id: "{{ automation_state_entity }}" data: value: > {% set new_automation_state = (automation_state | combine({ state_motion_light_state: automation_state_none })) %} {{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }} - choose: - conditions: - condition: template value_template: "{{ disable_action != [] }}" sequence: !input disable_action