feat: add Presence Scene Controller and Time Of Day Selector blueprints
- Presence Scene Controller: per-room presence-aware time-of-day scenes with vacant/sleep scenes and Motion Light coexistence - Time Of Day Selector: event-driven state machine that sets an input_select when a configured time entity fires - List both blueprints in the root README - Bump version to 2.13.0
This commit is contained in:
@@ -0,0 +1,626 @@
|
||||
# Presence Scene Controller Blueprint
|
||||
# Per-room presence-aware time-of-day scene controller.
|
||||
# See README.md for detailed documentation.
|
||||
#
|
||||
# Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com)
|
||||
|
||||
blueprint:
|
||||
name: "Custom: Presence Scene Controller"
|
||||
description: >
|
||||
Per-room presence-aware time-of-day scene controller. Maps scenes to
|
||||
time-of-day options by index (like Day Scene Controller), but adds
|
||||
presence gating, vacant/sleep scenes, and Motion Light coexistence.
|
||||
Designed to take over the per-room responsibilities of Day Scene
|
||||
Controller — keep Day Scene Controller for genuinely house-wide
|
||||
scenes (Away, Goodnight, All-Off).
|
||||
domain: automation
|
||||
|
||||
input:
|
||||
# -------------------------------------------------------------------------
|
||||
# State Sources
|
||||
# -------------------------------------------------------------------------
|
||||
states_group:
|
||||
name: States
|
||||
collapsed: false
|
||||
input:
|
||||
time_of_day_state:
|
||||
name: Time of Day State Selector
|
||||
description: >
|
||||
input_select entity holding the current time-of-day state.
|
||||
Typically managed by Time of Day Selector or Time of Day
|
||||
Controller.
|
||||
selector:
|
||||
entity:
|
||||
domain: input_select
|
||||
|
||||
room_enable_switch:
|
||||
name: Room Enable Switch (optional)
|
||||
description: >
|
||||
When OFF, the vacant scene is applied and TOD/presence are
|
||||
ignored. Useful as a per-room kill switch (movie night, guest
|
||||
bedroom). Leave empty to skip this gate.
|
||||
default: ""
|
||||
selector:
|
||||
entity:
|
||||
domain:
|
||||
- binary_sensor
|
||||
- input_boolean
|
||||
- switch
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Presence
|
||||
# -------------------------------------------------------------------------
|
||||
presence_group:
|
||||
name: Presence
|
||||
collapsed: false
|
||||
input:
|
||||
presence_sensors:
|
||||
name: Presence Sensors (optional)
|
||||
description: >
|
||||
Any presence-related entities — motion sensors, mmWave,
|
||||
occupancy groups, input_booleans. ANY-on logic: the room is
|
||||
occupied if any sensor reports on. Sensors reporting
|
||||
unavailable/unknown are treated as on (safe default).
|
||||
Leave empty to disable presence gating; the room is then
|
||||
always treated as occupied and the vacant scene is never
|
||||
applied.
|
||||
default: []
|
||||
selector:
|
||||
entity:
|
||||
domain:
|
||||
- binary_sensor
|
||||
- input_boolean
|
||||
- group
|
||||
multiple: true
|
||||
|
||||
presence_off_timeout:
|
||||
name: Presence Off Timeout (seconds)
|
||||
description: >
|
||||
How long all presence sensors must report off before the
|
||||
room is considered vacant. Filters PIR drop-outs and short
|
||||
absences.
|
||||
default: 120
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 3600
|
||||
step: 5
|
||||
unit_of_measurement: seconds
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Scenes
|
||||
# -------------------------------------------------------------------------
|
||||
scenes_group:
|
||||
name: Scenes
|
||||
collapsed: false
|
||||
input:
|
||||
scenes:
|
||||
name: Time-of-Day Scenes
|
||||
description: >
|
||||
Ordered list of scenes matching time-of-day options by index.
|
||||
Length must equal the number of time-of-day options.
|
||||
selector:
|
||||
entity:
|
||||
domain: scene
|
||||
multiple: true
|
||||
|
||||
vacant_scene:
|
||||
name: Vacant Scene
|
||||
description: >
|
||||
Scene applied when the room becomes vacant (after the
|
||||
presence-off timeout) or when the room enable switch is OFF.
|
||||
Typically a dim or all-off scene.
|
||||
selector:
|
||||
entity:
|
||||
domain: scene
|
||||
|
||||
sleep_scene:
|
||||
name: Sleep Scene (optional)
|
||||
description: >
|
||||
Scene applied when the sleep mode switch is ON. Bypasses TOD
|
||||
and presence entirely. Typically a very dim "do not disturb"
|
||||
scene or all-off. Leave empty if sleep mode is not used.
|
||||
default: ""
|
||||
selector:
|
||||
entity:
|
||||
domain: scene
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Behavior
|
||||
# -------------------------------------------------------------------------
|
||||
behavior_group:
|
||||
name: Behavior
|
||||
collapsed: false
|
||||
input:
|
||||
tod_behavior_while_occupied:
|
||||
name: TOD Change Behavior While Occupied
|
||||
description: >
|
||||
What to do when time-of-day flips while the room is occupied:
|
||||
|
||||
- apply_if_lights_off (default): re-apply only if every
|
||||
targeted light/switch in the new scene is currently off.
|
||||
Doesn't disturb a sleeping/relaxing user, refreshes a
|
||||
dark-but-occupied room.
|
||||
|
||||
- defer: never re-apply on TOD flip; wait for the next
|
||||
vacancy cycle. Best for bedrooms with mmWave that always
|
||||
reports occupancy.
|
||||
|
||||
- apply: always re-apply on TOD flip. Best for kitchens,
|
||||
hallways, transit areas.
|
||||
default: apply_if_lights_off
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- apply_if_lights_off
|
||||
- defer
|
||||
- apply
|
||||
|
||||
sleep_mode_switch:
|
||||
name: Sleep Mode Switch (optional)
|
||||
description: >
|
||||
input_boolean / binary_sensor that, when ON, forces the
|
||||
sleep scene and ignores TOD/presence. Useful for shift
|
||||
workers, daytime naps, or letting a partner enter without
|
||||
lighting up the room. Requires sleep_scene to be configured
|
||||
(otherwise the switch is silently ignored).
|
||||
default: ""
|
||||
selector:
|
||||
entity:
|
||||
domain:
|
||||
- binary_sensor
|
||||
- input_boolean
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Motion Light Coexistence (advanced)
|
||||
# -------------------------------------------------------------------------
|
||||
motion_light_group:
|
||||
name: Motion Light Coexistence
|
||||
collapsed: true
|
||||
input:
|
||||
motion_light_state_entity:
|
||||
name: Motion Light State Entity (optional)
|
||||
description: >
|
||||
input_text used by a Motion Light blueprint instance that
|
||||
targets the same lights as this room. While Motion Light is
|
||||
actively controlling the lights (state ENABLING/ENABLED/
|
||||
MANUAL), this blueprint stays out of its way. State changes
|
||||
on this entity also abort any in-flight scene application
|
||||
via mode: restart.
|
||||
default: ""
|
||||
selector:
|
||||
entity:
|
||||
domain: input_text
|
||||
|
||||
motion_light_state_key:
|
||||
name: Motion Light State Key (optional)
|
||||
description: >
|
||||
JSON key used inside Motion Light's state entity. Leave
|
||||
empty to fall back to the room enable switch entity ID; set
|
||||
explicitly to match Motion Light's
|
||||
automation_state_placeholder_key.
|
||||
default: ""
|
||||
selector:
|
||||
text:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Persistence
|
||||
# -------------------------------------------------------------------------
|
||||
persistence_group:
|
||||
name: Persistence
|
||||
collapsed: true
|
||||
input:
|
||||
last_applied_state_entity:
|
||||
name: Last Applied Scene Entity (optional)
|
||||
description: >
|
||||
input_text used to remember the last scene this automation
|
||||
applied, so identical re-applications can be skipped. Each
|
||||
instance must have its own entity. Default max_length 100
|
||||
is usually enough; ensure it fits the longest scene entity
|
||||
ID. Leave empty to disable the skip-if-same-scene guard.
|
||||
default: ""
|
||||
selector:
|
||||
entity:
|
||||
domain: input_text
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Callbacks
|
||||
# -------------------------------------------------------------------------
|
||||
actions_group:
|
||||
name: Actions
|
||||
collapsed: true
|
||||
input:
|
||||
scene_applied_callback:
|
||||
name: Scene Applied Callback
|
||||
description: >
|
||||
Runs after a TOD or sleep scene is applied. Useful for
|
||||
notifications, dependent automations.
|
||||
default: []
|
||||
selector:
|
||||
action: {}
|
||||
|
||||
vacant_scene_applied_callback:
|
||||
name: Vacant Scene Applied Callback
|
||||
description: Runs after the vacant scene is applied.
|
||||
default: []
|
||||
selector:
|
||||
action: {}
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Debug
|
||||
# -------------------------------------------------------------------------
|
||||
debug_group:
|
||||
name: Debug
|
||||
collapsed: true
|
||||
input:
|
||||
enable_debug_notifications:
|
||||
name: Enable Debug Notifications
|
||||
description: >
|
||||
Send a persistent notification on every evaluation with the
|
||||
decision inputs and the resolved target scene.
|
||||
default: false
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
# Restart mode: rapid TOD/presence changes always use latest values, and
|
||||
# Motion Light state changes can abort an in-flight scene application.
|
||||
mode: restart
|
||||
|
||||
# =============================================================================
|
||||
# Triggers
|
||||
# =============================================================================
|
||||
trigger:
|
||||
# Time-of-day state changed
|
||||
- platform: state
|
||||
entity_id: !input time_of_day_state
|
||||
id: tod_changed
|
||||
|
||||
# Any presence sensor reports on (immediate)
|
||||
- platform: state
|
||||
entity_id: !input presence_sensors
|
||||
to: "on"
|
||||
id: presence_on
|
||||
|
||||
# All presence sensors have been off for the configured timeout
|
||||
- platform: state
|
||||
entity_id: !input presence_sensors
|
||||
to: "off"
|
||||
for:
|
||||
seconds: !input presence_off_timeout
|
||||
id: presence_off
|
||||
|
||||
# Room enable switch toggled
|
||||
- platform: state
|
||||
entity_id: !input room_enable_switch
|
||||
id: room_enable_changed
|
||||
|
||||
# Sleep mode switch toggled
|
||||
- platform: state
|
||||
entity_id: !input sleep_mode_switch
|
||||
id: sleep_mode_changed
|
||||
|
||||
# Motion Light state entity changed (abort signal via mode: restart)
|
||||
- platform: state
|
||||
entity_id: !input motion_light_state_entity
|
||||
id: motion_light_state_changed
|
||||
|
||||
# =============================================================================
|
||||
# Variables
|
||||
# =============================================================================
|
||||
variables:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Inputs
|
||||
# ---------------------------------------------------------------------------
|
||||
time_of_day_state: !input time_of_day_state
|
||||
presence_sensors: !input presence_sensors
|
||||
room_enable_switch: !input room_enable_switch
|
||||
scenes_list: !input scenes
|
||||
vacant_scene: !input vacant_scene
|
||||
sleep_scene: !input sleep_scene
|
||||
sleep_mode_switch: !input sleep_mode_switch
|
||||
tod_behavior_while_occupied: !input tod_behavior_while_occupied
|
||||
motion_light_state_entity: !input motion_light_state_entity
|
||||
motion_light_state_key_input: !input motion_light_state_key
|
||||
last_applied_state_entity: !input last_applied_state_entity
|
||||
enable_debug_notifications: !input enable_debug_notifications
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trigger context
|
||||
# ---------------------------------------------------------------------------
|
||||
trigger_id: "{{ trigger.id | default('') }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TOD resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
tod_state: "{{ states(time_of_day_state) }}"
|
||||
tod_options: "{{ state_attr(time_of_day_state, 'options') or [] }}"
|
||||
tod_index: "{{ tod_options.index(tod_state) if tod_state in tod_options else -1 }}"
|
||||
tod_valid: "{{ tod_index >= 0 and tod_index < (scenes_list | length) }}"
|
||||
tod_scene: "{{ scenes_list[tod_index] if tod_valid else '' }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Presence resolution (ANY-on logic)
|
||||
# Sensors reporting unavailable/unknown are treated as occupied — safer than
|
||||
# the alternative of plunging an actually-occupied room into vacant scene.
|
||||
# ---------------------------------------------------------------------------
|
||||
has_presence_sensors: "{{ (presence_sensors | length) > 0 }}"
|
||||
any_presence_on: >
|
||||
{% set sensors = presence_sensors if presence_sensors is iterable else [presence_sensors] %}
|
||||
{% set ns = namespace(on=false) %}
|
||||
{% for s in sensors %}
|
||||
{% set v = states(s) %}
|
||||
{% if v in ['on', 'home', 'true', '1', 'unavailable', 'unknown'] %}
|
||||
{% set ns.on = true %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ ns.on }}
|
||||
is_occupied: "{{ (not has_presence_sensors) or any_presence_on }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Room enable / sleep mode
|
||||
# ---------------------------------------------------------------------------
|
||||
room_enabled: >
|
||||
{% if room_enable_switch in [none, ''] %}
|
||||
true
|
||||
{% else %}
|
||||
{{ states(room_enable_switch) in ['on', 'home', 'true', '1'] }}
|
||||
{% endif %}
|
||||
|
||||
sleep_active: >
|
||||
{% if sleep_mode_switch in [none, ''] or sleep_scene in [none, ''] %}
|
||||
false
|
||||
{% else %}
|
||||
{{ states(sleep_mode_switch) in ['on', 'home', 'true', '1'] }}
|
||||
{% endif %}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Motion Light coexistence
|
||||
# State JSON shape (from Motion Light):
|
||||
# { "<key>": { "mls": "0|1|2|3", "mllat": "...", "mllb": ... } }
|
||||
# mls: 0=NONE, 1=ENABLED, 2=ENABLING, 3=MANUAL.
|
||||
# ---------------------------------------------------------------------------
|
||||
motion_light_state_global: >
|
||||
{% if motion_light_state_entity in [none, ''] %}
|
||||
{{ dict() }}
|
||||
{% else %}
|
||||
{% set text = states(motion_light_state_entity) | string %}
|
||||
{% if text in ['unknown', 'unavailable', 'none', ''] %}
|
||||
{{ dict() }}
|
||||
{% else %}
|
||||
{% set parsed = text | from_json(default=dict()) %}
|
||||
{{ parsed if parsed is mapping else dict() }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
motion_light_resolved_key: >
|
||||
{% if motion_light_state_key_input not in [none, ''] %}
|
||||
{{ motion_light_state_key_input }}
|
||||
{% else %}
|
||||
{{ room_enable_switch | default('', true) }}
|
||||
{% endif %}
|
||||
|
||||
motion_light_room_state: >
|
||||
{% set inner = motion_light_state_global.get(motion_light_resolved_key, dict()) %}
|
||||
{{ (inner.get('mls', '0') | string) if inner is mapping else '0' }}
|
||||
|
||||
motion_light_claimed: "{{ motion_light_room_state in ['1', '2', '3'] }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Target scene (recomputed at apply time, never cached at trigger time)
|
||||
# ---------------------------------------------------------------------------
|
||||
target_scene: >
|
||||
{% if sleep_active %}
|
||||
{{ sleep_scene }}
|
||||
{% elif not room_enabled %}
|
||||
{{ vacant_scene }}
|
||||
{% elif is_occupied and tod_valid %}
|
||||
{{ tod_scene }}
|
||||
{% else %}
|
||||
{{ vacant_scene }}
|
||||
{% endif %}
|
||||
|
||||
is_vacant_target: "{{ target_scene == vacant_scene }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Skip-if-same-scene guard
|
||||
# ---------------------------------------------------------------------------
|
||||
last_applied_scene: >
|
||||
{% if last_applied_state_entity in [none, ''] %}
|
||||
''
|
||||
{% else %}
|
||||
{{ states(last_applied_state_entity) | string }}
|
||||
{% endif %}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# `apply_if_lights_off` evaluation: are all entities in the target scene
|
||||
# currently off? Looks at the scene's entity_id attribute.
|
||||
# ---------------------------------------------------------------------------
|
||||
scene_lights_off: >
|
||||
{% set ns = namespace(any_on=false) %}
|
||||
{% if target_scene not in [none, ''] %}
|
||||
{% set entities = state_attr(target_scene, 'entity_id') or [] %}
|
||||
{% for e in entities %}
|
||||
{% if (e.startswith('light.') or e.startswith('switch.')) and is_state(e, 'on') %}
|
||||
{% set ns.any_on = true %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ not ns.any_on }}
|
||||
|
||||
# =============================================================================
|
||||
# Actions
|
||||
# =============================================================================
|
||||
action:
|
||||
# ---------------------------------------------------------------------------
|
||||
# Debug: log entry state
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions: "{{ enable_debug_notifications }}"
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "Presence Scene Controller — entry"
|
||||
message: >
|
||||
Trigger: {{ trigger_id }}
|
||||
TOD: {{ tod_state }} (idx {{ tod_index }})
|
||||
Occupied: {{ is_occupied }} | Enabled: {{ room_enabled }} | Sleep: {{ sleep_active }}
|
||||
ML claimed: {{ motion_light_claimed }} (mls={{ motion_light_room_state }}, key={{ motion_light_resolved_key }})
|
||||
Target: {{ target_scene }}
|
||||
Last applied: {{ last_applied_scene }}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yield to Motion Light if it's currently controlling the room
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ motion_light_claimed }}"
|
||||
sequence:
|
||||
- stop: "Motion Light is controlling this room — yielding."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Motion Light state-change is purely an abort signal. If we reach here and
|
||||
# ML is NOT claimed, do nothing — ML may have just released the room and
|
||||
# turned the lights off intentionally; we should not immediately re-apply.
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger_id == 'motion_light_state_changed' }}"
|
||||
sequence:
|
||||
- stop: "Motion Light state changed — abort signal only."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validate TOD configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ tod_index == -1 }}"
|
||||
sequence:
|
||||
- stop: "Invalid time-of-day state — current state not in options list."
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ is_occupied and room_enabled and not sleep_active and not tod_valid }}"
|
||||
sequence:
|
||||
- stop: "Scene index out of range — scenes list shorter than TOD options."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TOD-while-occupied policy. Only kicks in when this run was triggered by a
|
||||
# TOD flip AND the room is occupied AND no override mode is in effect.
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ trigger_id == 'tod_changed'
|
||||
and is_occupied
|
||||
and room_enabled
|
||||
and not sleep_active }}
|
||||
sequence:
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ tod_behavior_while_occupied == 'defer' }}"
|
||||
sequence:
|
||||
- stop: "TOD changed while occupied — deferring per policy."
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ tod_behavior_while_occupied == 'apply_if_lights_off' and not scene_lights_off }}
|
||||
sequence:
|
||||
- stop: "TOD changed while occupied with lights on — deferring."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Skip if the target scene is already what we last applied
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ last_applied_scene != ''
|
||||
and target_scene not in [none, '']
|
||||
and last_applied_scene == target_scene }}
|
||||
sequence:
|
||||
- stop: "Scene already applied — no-op."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Apply the target scene
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ target_scene not in [none, ''] }}"
|
||||
sequence:
|
||||
- service: scene.turn_on
|
||||
target:
|
||||
entity_id: "{{ target_scene }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Persist last-applied scene
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ last_applied_state_entity not in [none, '']
|
||||
and target_scene not in [none, ''] }}
|
||||
sequence:
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: "{{ last_applied_state_entity }}"
|
||||
data:
|
||||
value: "{{ target_scene }}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-apply Motion Light re-check. Brief delay then re-read ML state — if
|
||||
# Motion Light claimed the room while we were applying, do not contest.
|
||||
# ---------------------------------------------------------------------------
|
||||
- delay:
|
||||
milliseconds: 300
|
||||
|
||||
- variables:
|
||||
ml_state_after: >
|
||||
{% if motion_light_state_entity in [none, ''] %}
|
||||
0
|
||||
{% else %}
|
||||
{% set text = states(motion_light_state_entity) | string %}
|
||||
{% if text in ['unknown', 'unavailable', 'none', ''] %}
|
||||
0
|
||||
{% else %}
|
||||
{% set parsed = text | from_json(default=dict()) %}
|
||||
{% if parsed is mapping %}
|
||||
{% set inner = parsed.get(motion_light_resolved_key, dict()) %}
|
||||
{{ (inner.get('mls', '0') | string) if inner is mapping else '0' }}
|
||||
{% else %}
|
||||
0
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ (ml_state_after | string) in ['1', '2', '3'] }}"
|
||||
sequence:
|
||||
- choose:
|
||||
- conditions: "{{ enable_debug_notifications }}"
|
||||
sequence:
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "Presence Scene Controller"
|
||||
message: "Motion Light claimed room post-apply — yielding without callback."
|
||||
- stop: "Motion Light took over after scene apply."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Run the appropriate callback
|
||||
# ---------------------------------------------------------------------------
|
||||
- choose:
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ is_vacant_target }}"
|
||||
sequence: !input vacant_scene_applied_callback
|
||||
|
||||
default: !input scene_applied_callback
|
||||
Reference in New Issue
Block a user