[Claude] - Analyze Motion Light.yaml file designed to work as automation blueprint for Home Assistant OS. Refactor it improving overall code quality, fix obvious or critical bugs/mistakes, fix spelling if required and add comments that will make the code more easy to read and understand.
This commit is contained in:
@@ -1,35 +1,84 @@
|
||||
# =============================================================================
|
||||
# Motion Light Automation Blueprint for Home Assistant
|
||||
# =============================================================================
|
||||
# This blueprint creates a smart motion-activated light control system.
|
||||
# It handles motion detection, luminance-based triggering, and manual override
|
||||
# detection to provide intelligent lighting automation.
|
||||
#
|
||||
# Features:
|
||||
# - Multiple motion sensor support (triggers on ANY sensor)
|
||||
# - Condition switches (ALL must be ON for automation to work)
|
||||
# - Light and/or switch control
|
||||
# - Configurable timeout delay before turning off
|
||||
# - Luminance sensor support (only trigger in dark conditions)
|
||||
# - Manual override detection (stops automation if user changes light)
|
||||
# - Brightness threshold (only trigger if light is dim)
|
||||
# - Custom light parameters (brightness, color, etc.)
|
||||
# - Callback actions for enable/disable/manual events
|
||||
#
|
||||
# State Machine:
|
||||
# The automation tracks these states via persistent storage:
|
||||
# - NONE (0): Idle, waiting for motion
|
||||
# - ENABLING (2): Light turn-on command sent, waiting for state change
|
||||
# - ENABLED (1): Light is ON and controlled by automation
|
||||
# - MANUAL (3): User took control, automation paused until light turns off
|
||||
#
|
||||
# Behavior Notes:
|
||||
# - Will NOT turn on light if it's already ON (prevents hijacking user control)
|
||||
# - If user changes light while automation is active, enters MANUAL mode
|
||||
# - MANUAL mode exits when light is turned OFF (by any means)
|
||||
# - Timeout delay only applies when turning OFF (motion cleared)
|
||||
#
|
||||
# Requirements:
|
||||
# - At least one motion sensor
|
||||
# - input_text entity for persistent state storage
|
||||
# - Target light and/or switch to control
|
||||
#
|
||||
# Author: Custom Blueprint
|
||||
# =============================================================================
|
||||
|
||||
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.
|
||||
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 trigger until the light is turned off.
|
||||
Note: Not tested when motion sensors and state sensors are used at the same time.
|
||||
domain: automation
|
||||
|
||||
# ===========================================================================
|
||||
# INPUT CONFIGURATION
|
||||
# ===========================================================================
|
||||
input:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Motion & Condition Sensors
|
||||
# -------------------------------------------------------------------------
|
||||
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.
|
||||
description: >
|
||||
Select one or more motion sensors. Light turns ON if ANY sensor
|
||||
detects motion (OR logic).
|
||||
default: []
|
||||
selector:
|
||||
entity:
|
||||
domain:
|
||||
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
|
||||
Automation will not trigger if ANY of these switches is OFF.
|
||||
All must be ON for the automation to work (AND logic).
|
||||
default: []
|
||||
selector:
|
||||
entity:
|
||||
@@ -41,24 +90,28 @@ blueprint:
|
||||
- binary_sensor
|
||||
multiple: true
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Target Devices
|
||||
# -------------------------------------------------------------------------
|
||||
devices_group:
|
||||
name: "Devices"
|
||||
collapsed: false
|
||||
input:
|
||||
target_light:
|
||||
name: Target Light (optional)
|
||||
description: "Light to control. Setup no light or single light."
|
||||
description: "Light to control. Supports single light only."
|
||||
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.
|
||||
Provide a YAML dictionary of light.turn_on parameters.
|
||||
If not specified, the light's last settings are preserved.
|
||||
Example:
|
||||
brightness: 200
|
||||
color_temp: 350
|
||||
@@ -66,55 +119,72 @@ blueprint:
|
||||
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'
|
||||
description: >
|
||||
Automation only triggers if the light's current brightness is
|
||||
below this value. Set to 0 to disable this check.
|
||||
default: 0
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 255
|
||||
step: 1
|
||||
|
||||
step: 1
|
||||
|
||||
target_switch:
|
||||
name: Target Switch (optional)
|
||||
description: "Switch to control. Setup no switch or single switch."
|
||||
description: "Switch to control. Supports single switch only."
|
||||
default: []
|
||||
selector:
|
||||
entity:
|
||||
domain: switch
|
||||
multiple: true
|
||||
|
||||
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
|
||||
description: >
|
||||
Delay before turning off the light after all motion sensors
|
||||
clear. Set to 0 for immediate turn off.
|
||||
default: 0
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 3600
|
||||
step: 1
|
||||
unit_of_measurement: seconds
|
||||
|
||||
unit_of_measurement: seconds
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Persistent State Configuration
|
||||
# -------------------------------------------------------------------------
|
||||
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.`"
|
||||
description: >
|
||||
`input_text` entity that stores the automation state in JSON format.
|
||||
Required for manual override detection and state tracking.
|
||||
Doesn't require specific initial state - values can be empty.
|
||||
Each automation instance must have its own 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`
|
||||
description: >
|
||||
Overrides key for persistent storage if not empty.
|
||||
By default uses the target light/switch entity ID.
|
||||
Don't override if you don't understand the meaning.
|
||||
default: ''
|
||||
selector:
|
||||
text:
|
||||
|
||||
text:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Luminance Control
|
||||
# -------------------------------------------------------------------------
|
||||
luminance:
|
||||
name: "Luminance"
|
||||
collapsed: true
|
||||
@@ -129,7 +199,9 @@ blueprint:
|
||||
|
||||
luminance_threshold:
|
||||
name: Luminance threshold (optional)
|
||||
description: Light will only turn on if sensor value is below this threshold
|
||||
description: >
|
||||
Light will only turn on if luminance sensor value is below
|
||||
this threshold (darker than this level).
|
||||
default: 100
|
||||
selector:
|
||||
number:
|
||||
@@ -140,7 +212,9 @@ blueprint:
|
||||
|
||||
luminance_enable_switch:
|
||||
name: Luminance control enable switch (optional)
|
||||
description: Switch or input_boolean to enable/disable luminance control
|
||||
description: >
|
||||
Switch or input_boolean to enable/disable luminance-based control.
|
||||
When OFF, luminance check is skipped.
|
||||
default: null
|
||||
selector:
|
||||
entity:
|
||||
@@ -148,6 +222,9 @@ blueprint:
|
||||
- switch
|
||||
- input_boolean
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Callback Actions
|
||||
# -------------------------------------------------------------------------
|
||||
actions:
|
||||
name: "Actions"
|
||||
collapsed: true
|
||||
@@ -158,40 +235,48 @@ blueprint:
|
||||
default: []
|
||||
selector:
|
||||
condition: {}
|
||||
|
||||
|
||||
enable_action:
|
||||
name: Enable callback action (optional)
|
||||
description: Runs when light is turned on
|
||||
description: Runs when light is turned ON by this automation
|
||||
default: []
|
||||
selector:
|
||||
action: {}
|
||||
|
||||
|
||||
disable_action:
|
||||
name: Disable callback action (optional)
|
||||
description: Runs when light is turned off
|
||||
description: Runs when light is turned OFF by this automation
|
||||
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`.
|
||||
If checked, entering manual mode will also execute the
|
||||
disable callback 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.
|
||||
description: >
|
||||
Runs when user manually changes the light while automation is active.
|
||||
Requires 'Automation state entity' to be configured.
|
||||
default: []
|
||||
selector:
|
||||
action: {}
|
||||
|
||||
# =============================================================================
|
||||
# AUTOMATION MODE
|
||||
# =============================================================================
|
||||
# Restart mode ensures rapid motion events don't queue up
|
||||
mode: restart
|
||||
|
||||
# =============================================================================
|
||||
# TRIGGERS
|
||||
# =============================================================================
|
||||
trigger:
|
||||
# Motion sensors ON/OFF
|
||||
- platform: state
|
||||
@@ -200,19 +285,19 @@ trigger:
|
||||
|
||||
# Condition switches ON/OFF
|
||||
- platform: state
|
||||
entity_id: !input condition_switches
|
||||
|
||||
# Light ON/OFF
|
||||
entity_id: !input condition_switches
|
||||
|
||||
# Light state changed (for manual override detection)
|
||||
- platform: state
|
||||
entity_id: !input target_light
|
||||
id: "light_state_changed"
|
||||
|
||||
# Switches ON/OFF
|
||||
|
||||
# Switch state changed (for manual override detection)
|
||||
- platform: state
|
||||
entity_id: !input target_switch
|
||||
id: "switch_state_changed"
|
||||
|
||||
# Luminance sensor ON/OFF
|
||||
id: "switch_state_changed"
|
||||
|
||||
# Luminance sensor value changed
|
||||
- platform: template
|
||||
value_template: >
|
||||
{% if luminance_sensor %}
|
||||
@@ -220,6 +305,8 @@ trigger:
|
||||
{% else %}
|
||||
false
|
||||
{% endif %}
|
||||
|
||||
# Luminance enable switch changed
|
||||
- platform: template
|
||||
value_template: >
|
||||
{% if luminance_enable_switch %}
|
||||
@@ -228,46 +315,65 @@ trigger:
|
||||
false
|
||||
{% endif %}
|
||||
|
||||
# =============================================================================
|
||||
# CONDITIONS
|
||||
# =============================================================================
|
||||
condition: !input user_condition
|
||||
|
||||
# TOFIX:
|
||||
# - state_sensors
|
||||
# - might be problems with storing persistent state
|
||||
|
||||
# =============================================================================
|
||||
# VARIABLES
|
||||
# =============================================================================
|
||||
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'
|
||||
# ---------------------------------------------------------------------------
|
||||
# Debug Flags
|
||||
# ---------------------------------------------------------------------------
|
||||
is_debug: false # Detailed debug for specific actions
|
||||
is_base_debug: false # Basic debug info at start
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# State Machine Constants
|
||||
# ---------------------------------------------------------------------------
|
||||
# These define the possible automation states stored in persistent storage
|
||||
automation_state_invalid: '-1' # Error state
|
||||
automation_state_none: '0' # Idle, waiting for motion
|
||||
automation_state_enabled: '1' # Light is ON and controlled by automation
|
||||
automation_state_enabling: '2' # Turn-on command sent, awaiting confirmation
|
||||
automation_state_manual: '3' # User took control, automation paused
|
||||
|
||||
# Persistent state JSON keys
|
||||
state_motion_light_state: 'mls' # Current state machine state
|
||||
state_motion_light_last_action_timestamp: 'mllat' # Last action timestamp
|
||||
state_motion_light_last_brightness: 'mllb' # Brightness before automation
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trigger Context
|
||||
# ---------------------------------------------------------------------------
|
||||
date_time_now: "{{ now() }}"
|
||||
trigger_id: "{{ trigger.id }}"
|
||||
|
||||
# Defines
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Input Variables
|
||||
# ---------------------------------------------------------------------------
|
||||
sensors: !input motion_sensors
|
||||
condition_switches: !input condition_switches
|
||||
timeout: !input timeout_delay
|
||||
brightness_threshold: !input brightness_threshold
|
||||
|
||||
# Light
|
||||
|
||||
# Light configuration
|
||||
light_entities: !input target_light
|
||||
light_entity: "{{ light_entities[0] if light_entities | length != 0 else none }}"
|
||||
|
||||
# Switch
|
||||
|
||||
# Switch configuration
|
||||
switch_entities: !input target_switch
|
||||
switch_entity: "{{ switch_entities[0] if switch_entities | length != 0 else none }}"
|
||||
|
||||
# JSON global state.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Persistent State Management
|
||||
# ---------------------------------------------------------------------------
|
||||
automation_state_entity: !input automation_state_entity
|
||||
|
||||
# Parse global state JSON from input_text entity
|
||||
automation_state_global: >
|
||||
{% set text = states(automation_state_entity) | string %}
|
||||
{% if text in ['unknown','unavailable','none',''] %}
|
||||
@@ -275,7 +381,10 @@ variables:
|
||||
{% else %}
|
||||
{{ text | from_json }}
|
||||
{% endif %}
|
||||
|
||||
automation_state_placeholder_key: !input automation_state_placeholder_key
|
||||
|
||||
# Determine the key for this automation's state
|
||||
automation_state_key: >
|
||||
{% if automation_state_placeholder_key != '' %}
|
||||
{{ automation_state_placeholder_key }}
|
||||
@@ -284,32 +393,47 @@ variables:
|
||||
{% elif light_entity is not none %}
|
||||
{{ light_entity }}
|
||||
{% else %}
|
||||
'default_motion_light_placeholder'
|
||||
{{ 'default_motion_light_placeholder' }}
|
||||
{% endif %}
|
||||
automation_state: "{{ automation_state_global.get(automation_state_key, dict()) if light_entity != '' else dict() }}"
|
||||
|
||||
# Get this automation's state from global state
|
||||
# BUG FIX: Changed from 'light_entity != ""' to proper none check
|
||||
automation_state: "{{ automation_state_global.get(automation_state_key, dict()) if (light_entity is not none or switch_entity is not none) else dict() }}"
|
||||
|
||||
# Current state machine state
|
||||
motion_light_state: "{{ automation_state.get(state_motion_light_state, automation_state_none) }}"
|
||||
|
||||
# Track last action timestamp
|
||||
motion_light_last_action_timestamp: >
|
||||
{% if trigger_id == 'state_motion' %}
|
||||
{{ date_time_now }}
|
||||
{% else %}
|
||||
{{ (automation_state.get(state_motion_light_last_action_timestamp, none)) }}
|
||||
{{ automation_state.get(state_motion_light_last_action_timestamp, none) }}
|
||||
{% endif %}
|
||||
state_is_none: "{{ ((motion_light_state | string) == automation_state_none) }}"
|
||||
|
||||
# State machine state checks (for readability)
|
||||
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
|
||||
# ---------------------------------------------------------------------------
|
||||
# Callback 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 Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
luminance_sensor: !input luminance_sensor
|
||||
luminance_threshold: !input luminance_threshold
|
||||
luminance_enable_switch: !input luminance_enable_switch
|
||||
|
||||
# Check if luminance conditions allow triggering
|
||||
luminance_ok: >
|
||||
{% if luminance_sensor is not none and luminance_threshold is not none %}
|
||||
{% set val = states(luminance_sensor) | float(0) %}
|
||||
@@ -319,32 +443,55 @@ variables:
|
||||
{% endif %}
|
||||
{{ enabled and val < luminance_threshold }}
|
||||
{% else %}
|
||||
true
|
||||
{{ true }}
|
||||
{% endif %}
|
||||
|
||||
# Trigger details
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trigger Evaluation
|
||||
# ---------------------------------------------------------------------------
|
||||
# Check if ALL condition switches are ON (AND logic)
|
||||
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 }}
|
||||
{% if e | length == 0 %}
|
||||
{{ true }}
|
||||
{% else %}
|
||||
{{ (e | select('is_state', 'on') | list | length) == (e | length) }}
|
||||
{% endif %}
|
||||
|
||||
# Count how many motion sensors are currently detecting motion
|
||||
count_of_enabled_sensor: >
|
||||
{% set e = sensors if sensors is iterable else [sensors] %}
|
||||
{{ e | select('is_state', 'on') | list | length }}
|
||||
|
||||
# Motion state checks
|
||||
motion_on: "{{ count_of_enabled_sensor > 0 }}"
|
||||
motion_all_off: "{{ count_of_enabled_sensor == 0 }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Enable/Disable Decision Logic
|
||||
# ---------------------------------------------------------------------------
|
||||
# Should we enable the light? (All conditions must be met)
|
||||
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 }}
|
||||
|
||||
|
||||
# Should we disable the light? (Motion cleared OR condition switch turned off)
|
||||
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 }}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ACTIONS
|
||||
# =============================================================================
|
||||
action:
|
||||
# Debug info.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DEBUG: Log basic info (enable by setting is_base_debug: true)
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
@@ -352,61 +499,79 @@ action:
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "Debug Info"
|
||||
title: "Debug Info - Motion Light"
|
||||
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.
|
||||
trigger_id: {{ trigger.id }}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GUARDS: Validate prerequisites
|
||||
# ---------------------------------------------------------------------------
|
||||
# Guard: Only one light supported
|
||||
- choose:
|
||||
conditions:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ light_entities | length > 1}}"
|
||||
value_template: "{{ light_entities | length > 1 }}"
|
||||
sequence:
|
||||
stop: "Only one light is supported currently"
|
||||
|
||||
# Guard for 1 switch.
|
||||
- stop: "Only one light is supported currently"
|
||||
|
||||
# Guard: Only one switch supported
|
||||
- choose:
|
||||
conditions:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ switch_entities | length > 1}}"
|
||||
value_template: "{{ switch_entities | length > 1 }}"
|
||||
sequence:
|
||||
stop: "Only one switch is supported currently"
|
||||
|
||||
- stop: "Only one switch is supported currently"
|
||||
|
||||
# ===========================================================================
|
||||
# MAIN STATE MACHINE
|
||||
# ===========================================================================
|
||||
- choose:
|
||||
# Disable automation flag if light was changed during automation
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# CASE 1: Light/Switch State Changed (Manual Override Detection)
|
||||
# -----------------------------------------------------------------------
|
||||
# Handles state changes from the light/switch itself to detect
|
||||
# when automation turned it on vs when user manually changed it
|
||||
- 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)
|
||||
|
||||
# ----- Sub-case: Light/Switch turned OFF -----
|
||||
# Reset to NONE state so automation can work again
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{% set res = false %}
|
||||
{# BUG FIX: Changed from 'res = false' to 'res = true' for AND logic #}
|
||||
{% set res = true %}
|
||||
{% 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) %}
|
||||
{% set brightness = state_attr(light_entity, 'brightness') | int(0) %}
|
||||
{% set light_off = is_state(light_entity, 'off') or brightness < brightness_threshold %}
|
||||
{% set res = res and light_off %}
|
||||
{% endif %}
|
||||
{% if switch_entity is not none %}
|
||||
{% set res = res and is_state(switch_entity, 'off') %}
|
||||
{% endif %}
|
||||
{{ res }}
|
||||
|
||||
{# Only true if we have at least one device and all are off #}
|
||||
{{ res and (light_entity is not none or switch_entity is not none) }}
|
||||
|
||||
sequence:
|
||||
# Reset state to NONE
|
||||
- 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
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# ----- Sub-case: Automation just turned on the light -----
|
||||
# Transition from ENABLING to ENABLED
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ state_is_enabling }}"
|
||||
@@ -414,46 +579,54 @@ action:
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data:
|
||||
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
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# ----- Sub-case: User manually changed the light -----
|
||||
# Transition from ENABLED to MANUAL (user took control)
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ state_is_enabled }}"
|
||||
sequence:
|
||||
# BUG FIX: Fixed YAML structure - was 'data: >' instead of 'data:' with 'value: >'
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data: >
|
||||
data:
|
||||
value: >
|
||||
{% 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
|
||||
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
|
||||
|
||||
# Call disable action if configured
|
||||
- choose:
|
||||
- conditions: "{{ manual_action_runs_disable_action and disable_action != [] }}"
|
||||
sequence: !input disable_action
|
||||
|
||||
# Call manual action
|
||||
|
||||
# Call manual action callback
|
||||
- choose:
|
||||
- conditions: "{{ manual_action != [] }}"
|
||||
sequence: !input manual_action
|
||||
|
||||
# Enable path
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# CASE 2: Enable Path (Motion Detected, Should Turn On)
|
||||
# -----------------------------------------------------------------------
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ must_be_enabled }}"
|
||||
sequence:
|
||||
- choose:
|
||||
# Guard: stop if light already ON and automation_flag exists
|
||||
# Guard: Stop if light is already ON
|
||||
# (Don't hijack user-controlled light)
|
||||
- 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)) %}
|
||||
{# BUG FIX: Added proper null check for brightness #}
|
||||
{% set brightness = state_attr(light_entity, 'brightness') | int(0) %}
|
||||
{% set res = res or (is_state(light_entity, 'on') and brightness > brightness_threshold) %}
|
||||
{% endif %}
|
||||
{% if switch_entity is not none %}
|
||||
{% set res = res or is_state(switch_entity, 'on') %}
|
||||
@@ -461,11 +634,10 @@ action:
|
||||
{{ res }}
|
||||
sequence:
|
||||
- stop: "Light is already ON when sensors were triggered"
|
||||
|
||||
# Enable the light
|
||||
|
||||
# Enable the light/switch
|
||||
default:
|
||||
|
||||
# Debug info.
|
||||
# Debug info
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
@@ -473,61 +645,68 @@ action:
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "Debug Info (Must Be Enabled)"
|
||||
title: "Debug Info (Enable Path)"
|
||||
message: >
|
||||
Enabled. light_entity: {{ light_entity }}
|
||||
Enabling light. light_entity: {{ light_entity }}
|
||||
|
||||
# Store current brightness (to restore later if configured)
|
||||
- variables:
|
||||
last_brightness: >
|
||||
{% if (light_entity is none) or is_state(light_entity, 'off') %}
|
||||
0
|
||||
{% if light_entity is none or is_state(light_entity, 'off') %}
|
||||
{{ 0 }}
|
||||
{% else %}
|
||||
{{ state_attr(light_entity, 'brightness') }}
|
||||
{{ state_attr(light_entity, 'brightness') | int(0) }}
|
||||
{% 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.
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ light_entity is not none }}"
|
||||
sequence:
|
||||
- service: light.turn_on
|
||||
target:
|
||||
entity_id: "{{ light_entity }}"
|
||||
data: "{{ light_data if light_data else {} }}"
|
||||
|
||||
# Turn ON the switch
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ switch_entity is not none }}"
|
||||
sequence:
|
||||
- service: switch.turn_on
|
||||
target:
|
||||
entity_id: "{{ switch_entity }}"
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ switch_entity is not none }}"
|
||||
sequence:
|
||||
- service: switch.turn_on
|
||||
target:
|
||||
entity_id: "{{ switch_entity }}"
|
||||
|
||||
# Update state to ENABLING (waiting for light state change confirmation)
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
data:
|
||||
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
|
||||
{% 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 }}
|
||||
|
||||
# Execute enable callback action
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ enable_action != [] }}"
|
||||
sequence: !input enable_action
|
||||
|
||||
# Disable path
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# CASE 3: Disable Path (Motion Cleared, Should Turn Off)
|
||||
# -----------------------------------------------------------------------
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ must_be_disabled }}"
|
||||
sequence:
|
||||
|
||||
# Debug info.
|
||||
# Debug info
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
@@ -535,50 +714,52 @@ action:
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "Debug Info (Must Be Disabled)"
|
||||
title: "Debug Info (Disable Path)"
|
||||
message: >
|
||||
Disabled. light_entity: {{ light_entity }}
|
||||
|
||||
Disabling light. light_entity: {{ light_entity }}
|
||||
|
||||
# Wait for timeout before turning off
|
||||
- delay:
|
||||
seconds: "{{ timeout }}"
|
||||
|
||||
# Disable the light.
|
||||
|
||||
# Turn OFF or restore 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.
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ light_entity is not none }}"
|
||||
sequence:
|
||||
- variables:
|
||||
last_brightness: "{{ automation_state.get(state_motion_light_last_brightness, 0) | int }}"
|
||||
|
||||
- choose:
|
||||
# Restore previous brightness if it was set
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ last_brightness > 0 }}"
|
||||
sequence:
|
||||
- service: light.turn_on
|
||||
target:
|
||||
entity_id: "{{ light_entity }}"
|
||||
data:
|
||||
brightness: "{{ last_brightness }}"
|
||||
|
||||
# Otherwise turn off completely
|
||||
default:
|
||||
- service: light.turn_off
|
||||
target:
|
||||
entity_id: "{{ light_entity }}"
|
||||
|
||||
# Turn OFF 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.
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ switch_entity is not none }}"
|
||||
sequence:
|
||||
- service: switch.turn_off
|
||||
target:
|
||||
entity_id: "{{ switch_entity }}"
|
||||
|
||||
# Update state to NONE (ready for next motion event)
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ automation_state_entity }}"
|
||||
@@ -586,9 +767,10 @@ action:
|
||||
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 }}
|
||||
|
||||
|
||||
# Execute disable callback action
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ disable_action != [] }}"
|
||||
sequence: !input disable_action
|
||||
sequence: !input disable_action
|
||||
|
||||
Reference in New Issue
Block a user