Files
haos-blueprints/Zigbee/MQTT Light Selector.yaml

486 lines
19 KiB
YAML

# =============================================================================
# MQTT Light Selector Blueprint
# =============================================================================
# Cycles through a list of lights using MQTT button events (up/down/remind).
# The selected light flashes to provide visual feedback, then returns to its
# original state. Selection is persisted in an input_text helper.
#
# Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com)
# =============================================================================
blueprint:
name: "MQTT Light Selector"
description: >
Cycle through a list of lights using MQTT button events.
**Features:**
- Navigate lights with up/down actions
- Remind action flashes the currently selected light
- Visual feedback via configurable flash pattern
- Persists selection in an input_text helper
- Optional state persistence across restarts (JSON storage)
- Optional "remind on idle" - up/down acts as remind if idle too long
**How it works:**
1. Press up/down to cycle through the light list
2. Selected light flashes N times to confirm selection
3. Light returns to its original on/off state after flashing
domain: automation
input:
# -------------------------------------------------------------------------
# MQTT Device Configuration
# -------------------------------------------------------------------------
devices:
name: "MQTT Devices"
collapsed: false
input:
mqtt_topic:
name: Primary MQTT Topic
description: Main topic where button events are published (e.g., `zigbee2mqtt/button/action`)
selector:
text: {}
mqtt_topic2:
name: Secondary MQTT Topic (Optional)
description: >
Additional MQTT topic for a second device.
Leave as default placeholder if not using a second device.
default: "blueprint/disabled/mqtt_light_selector"
selector:
text: {}
# -------------------------------------------------------------------------
# Light Selection
# -------------------------------------------------------------------------
lights:
name: "Lights"
collapsed: false
input:
lights:
name: Lights to Cycle
description: >
List of lights to cycle through.
Order determines navigation sequence.
selector:
entity:
domain: light
multiple: true
# -------------------------------------------------------------------------
# Persistent State Configuration
# -------------------------------------------------------------------------
persistent_state:
name: "Persistent State"
collapsed: false
input:
selected_light_helper:
name: Selected Light Helper
description: >
Input_text entity to store the currently selected light entity ID.
Create one via Settings → Devices & Services → Helpers.
selector:
entity:
domain: input_text
automation_state_entity:
name: Automation State Entity (Optional)
description: >
Input_text entity for storing automation state as JSON.
Used to remember light state during flash sequence.
Leave empty to disable state persistence.
default: null
selector:
entity:
domain: input_text
# -------------------------------------------------------------------------
# Action ID Mapping
# -------------------------------------------------------------------------
action_ids:
name: "Action IDs"
collapsed: false
input:
action_up:
name: Next Light Action ID
description: MQTT payload action value for selecting the next light in the list.
default: ""
selector:
text: {}
action_down:
name: Previous Light Action ID
description: MQTT payload action value for selecting the previous light in the list.
default: ""
selector:
text: {}
action_remind:
name: Remind Action ID (Optional)
description: >
MQTT payload action value for flashing the current selection without changing it.
Leave empty to disable.
default: ""
selector:
text: {}
# -------------------------------------------------------------------------
# Behavior Parameters
# -------------------------------------------------------------------------
params:
name: "Parameters"
collapsed: false
input:
transition:
name: Light Transition Time
description: Duration of on/off transitions during flash sequence.
default: 0
selector:
number:
min: 0
max: 500
step: 10
unit_of_measurement: ms
remind_using_up_down_delay:
name: Idle Remind Threshold
description: >
If set, up/down actions will act as "remind" (flash current selection)
when more than this many seconds have passed since the last selection.
Set to 0 to disable this behavior.
default: 0
selector:
number:
min: 0
max: 100
step: 1
unit_of_measurement: s
flash_count:
name: Flash Count
description: Number of times to flash the selected light.
default: 2
selector:
number:
min: 1
max: 10
step: 1
flash_interval_ms:
name: Flash Interval
description: Time between each flash on/off cycle.
default: 500
selector:
number:
min: 100
max: 2000
step: 100
unit_of_measurement: ms
# -------------------------------------------------------------------------
# Advanced: Conditions & Callbacks
# -------------------------------------------------------------------------
actions_group:
name: "Actions"
collapsed: false
input:
condition_action:
name: Extra Condition
description: Optional condition that must be true for the automation to run.
default: []
selector:
condition: {}
callback_action:
name: Callback Action
description: Optional action to run after selecting a new light (before flashing).
default: []
selector:
action: {}
# =============================================================================
# Triggers: Listen for MQTT messages from configured devices
# =============================================================================
trigger:
- platform: mqtt
topic: !input mqtt_topic
id: "mqtt_primary"
- platform: mqtt
topic: !input mqtt_topic2
id: "mqtt_secondary"
# Apply user-defined condition before processing
condition: !input condition_action
# Restart mode ensures rapid button presses are handled correctly
mode: restart
# =============================================================================
# Variables: Configuration and state management
# =============================================================================
variables:
# ----- Debug flag (set to true for troubleshooting) -----
is_debug: false
# ----- Input references -----
lights: !input lights
helper: !input selected_light_helper
action_up: !input action_up
action_down: !input action_down
action_remind: !input action_remind
flash_count: !input flash_count
flash_interval_ms: !input flash_interval_ms
transition: !input transition
remind_using_up_down_delay: !input remind_using_up_down_delay
callback_action: !input callback_action
# ----- State persistence keys (short names to save space in JSON) -----
# lwo = last_was_on, ll = last_light, lsadt = last_select_action_datetime
state_key_last_was_on: "lwo"
state_key_last_light: "ll"
state_key_last_select_action_datetime: "lsadt"
# ----- Automation state entity and global state parsing -----
automation_state_entity: !input automation_state_entity
# Parse the JSON state from the helper entity (or return empty dict)
automation_state_global: >-
{% if automation_state_entity is not none %}
{% set text = states(automation_state_entity) | string %}
{% if text in ['unknown', 'unavailable', 'none', ''] %}
{{ dict() }}
{% else %}
{{ text | from_json }}
{% endif %}
{% else %}
{{ dict() }}
{% endif %}
current_datetime: "{{ now() }}"
# Unique key for this automation instance (based on first light in list)
# Note: Using entity_id avoids issues with special characters in MQTT topics
automation_state_key: "mqtt_light_selector:{{ lights[0] }}"
# Extract this automation's state from the global state object
automation_state: >-
{{ automation_state_global.get(automation_state_key, dict()) if automation_state_key != '' else dict() }}
# Retrieve persisted values (with defaults)
state_last_was_on: "{{ automation_state.get(state_key_last_was_on, false) | bool }}"
state_last_light: "{{ automation_state.get(state_key_last_light, '') | string }}"
state_last_select_action_datetime: >-
{{ as_datetime(automation_state.get(state_key_last_select_action_datetime, current_datetime)) }}
# ----- Current selection from helper -----
current_light: >-
{% set entity_id = states(helper) %}
{{ entity_id if entity_id in lights else none }}
current_index: >-
{{ lights.index(current_light) if current_light in lights else 0 }}
# =============================================================================
# Actions: Main automation logic
# =============================================================================
action:
# ---------------------------------------------------------------------------
# Debug: Log state information if debug mode is enabled
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "MQTT Light Selector Debug"
message: >
State key: {{ automation_state_key }}
Current light: {{ current_light }}
Current index: {{ current_index }}
Last light: {{ state_last_light }}
Last was on: {{ state_last_was_on }}
# ---------------------------------------------------------------------------
# MQTT Message Handler
# ---------------------------------------------------------------------------
- choose:
- conditions:
# Handle messages from either MQTT trigger
- condition: template
value_template: "{{ trigger.id in ['mqtt_primary', 'mqtt_secondary'] }}"
sequence:
# Extract action ID from MQTT payload
- variables:
action_id: "{{ trigger.payload_json.action }}"
# -----------------------------------------------------------------
# Step 1: Restore previous light state if interrupted mid-flash
# -----------------------------------------------------------------
# If automation was restarted during a flash sequence, restore
# the light to its original state before proceeding
- choose:
- conditions:
- condition: template
value_template: "{{ state_last_light != '' }}"
sequence:
# Restore light to its previous on/off state
- choose:
- conditions:
- condition: template
value_template: "{{ state_last_was_on }}"
sequence:
- service: light.turn_on
target:
entity_id: "{{ state_last_light }}"
data:
transition: "{{ transition }}"
default:
- service: light.turn_off
target:
entity_id: "{{ state_last_light }}"
data:
transition: "{{ transition }}"
# Clear the "last light" from state since we've restored it
- choose:
- conditions:
- condition: template
value_template: "{{ automation_state_entity is not none }}"
sequence:
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >-
{% set new_state = automation_state | combine({
state_key_last_light: '',
state_key_last_was_on: state_last_was_on,
state_key_last_select_action_datetime: current_datetime | string
}) %}
{{ automation_state_global | combine({ automation_state_key: new_state }) | tojson }}
# -----------------------------------------------------------------
# Step 2: Process selection action (up/down/remind)
# -----------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: >-
{{ action_id != '' and action_id in [action_up, action_down, action_remind] }}
sequence:
# Calculate the new selection
- variables:
# Time since last selection (for idle remind feature)
datetime_diff_seconds: >-
{{ (current_datetime - state_last_select_action_datetime).total_seconds() }}
# Determine step direction:
# - If idle too long and remind_using_up_down_delay is set, treat as remind (step=0)
# - Otherwise: up=+1, down=-1, remind=0
step: >-
{% if remind_using_up_down_delay > 0 and datetime_diff_seconds > remind_using_up_down_delay %}
0
{% elif action_up != '' and action_id == action_up %}
1
{% elif action_down != '' and action_id == action_down %}
-1
{% else %}
0
{% endif %}
# Calculate new index with wraparound
new_index: "{{ (current_index + step) % (lights | length) }}"
new_light: "{{ lights[new_index] }}"
# Remember if light was on before we start flashing
new_on: "{{ is_state(new_light, 'on') }}"
# Save state before flashing (to restore if interrupted)
- choose:
- conditions:
- condition: template
value_template: "{{ automation_state_entity is not none }}"
sequence:
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >-
{% set new_state = automation_state | combine({
state_key_last_light: new_light,
state_key_last_was_on: new_on,
state_key_last_select_action_datetime: current_datetime | string
}) %}
{{ automation_state_global | combine({ automation_state_key: new_state }) | tojson }}
# Run user-defined callback action (if provided)
- choose:
- conditions:
- condition: template
value_template: "{{ callback_action is defined and (callback_action | length > 0) }}"
sequence: !input callback_action
# Update the helper with the new selection
- service: input_text.set_value
target:
entity_id: "{{ helper }}"
data:
value: "{{ new_light }}"
# -----------------------------------------------------------------
# Flash sequence: Visual feedback for selection
# -----------------------------------------------------------------
- repeat:
count: "{{ flash_count }}"
sequence:
- service: light.turn_off
target:
entity_id: "{{ new_light }}"
data:
transition: "{{ transition }}"
- delay:
milliseconds: "{{ flash_interval_ms }}"
- service: light.turn_on
target:
entity_id: "{{ new_light }}"
data:
transition: "{{ transition }}"
- delay:
milliseconds: "{{ flash_interval_ms }}"
# Restore light to original state if it was off
- choose:
- conditions:
- condition: template
value_template: "{{ not new_on }}"
sequence:
- service: light.turn_off
target:
entity_id: "{{ new_light }}"
data:
transition: "{{ transition }}"
# Clear state after successful completion
- choose:
- conditions:
- condition: template
value_template: "{{ automation_state_entity is not none }}"
sequence:
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >-
{% set new_state = automation_state | combine({
state_key_last_light: '',
state_key_last_was_on: new_on,
state_key_last_select_action_datetime: current_datetime | string
}) %}
{{ automation_state_global | combine({ automation_state_key: new_state }) | tojson }}