# MQTT Light Selector Blueprint # Cycles through lights using MQTT button events with visual feedback. # See README.md for detailed documentation. # # 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 }}