Files
haos-blueprints/Common/Motion Light.yaml

594 lines
22 KiB
YAML

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