# MQTT Button Control Blueprint # Controls lights and switches using Zigbee2MQTT button devices. # See README.md for detailed documentation. # # Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com) blueprint: name: "Custom: MQTT Button Control" description: > Control a Zigbee2MQTT device with multiple actions: toggle lights, switches and input_booleans, and optionally remember the entity that was last interacted with in an input_text helper. domain: automation input: mqtt_group: name: "MQTT" collapsed: false input: mqtt_topic: name: MQTT Topic description: The MQTT topic for your Zigbee button (e.g., zigbee2mqtt/my_button1). selector: text: mqtt_topic2: name: MQTT Topic 2 (Optional) description: > Additional MQTT topic for a second button. Leave as default placeholder if not using. default: "blueprint/disabled/mqtt_button_control" selector: text: devices: name: "Primary" collapsed: false input: action_ids: name: Action IDs description: The action IDs that must map to lights (action → light) default: [] selector: text: multiple: true switches: name: Switches description: "The list of switches to control. Next types are supported: `light`, `switch`, `input_boolean`" default: [] selector: entity: domain: - light - switch - input_boolean multiple: true outputs: name: "Outputs" collapsed: false input: last_interacted_text: name: Last Interacted Entity Text Helper (optional) description: "`input_text` entity that will store last enabled entity" default: "" selector: entity: domain: input_text common: name: "Common" collapsed: false input: timeout_for_indication_blink: name: "Timeout for indicator blink" description: If not zero does blink when button with already enabled light is pressed to indicate that the light is active now if elapsed time from last action is greater then the specified value default: 10 selector: number: min: 0 max: 100 step: 1 unit_of_measurement: "s" blink_count: name: Count of blinks description: "Count of blinks to indicate active light" default: 3 selector: number: min: 0 max: 5 step: 1 blink_interval: name: Interval between blinks description: "Interval between indicator blinks (in ms)" default: 250 selector: number: min: 0 max: 1000 step: 50 unit_of_measurement: "ms" # ------------------------------------------------------------------------- # Debug # ------------------------------------------------------------------------- debug_group: name: "Debug" collapsed: true input: enable_debug_notifications: name: Enable Debug Notifications description: > Send persistent notifications for debugging automation behavior. Shows received action, resolved target entity and blink decision. default: false selector: boolean: trigger: - platform: mqtt topic: !input mqtt_topic id: button_1 - platform: mqtt topic: !input mqtt_topic2 id: button_2 enabled: "{{ mqtt_topic2 not in ['', 'blueprint/disabled/mqtt_button_control'] }}" mode: restart condition: - condition: template value_template: "{{ 'action' in trigger.payload_json }}" action: - variables: action_id: "{{ trigger.payload_json.action }}" switches: !input switches action_ids: !input action_ids last_interacted_text: !input last_interacted_text is_debug: !input enable_debug_notifications # Resolve action_id → switch index. Whitespace-controlled and coerced # to int so subscripting `switches[target_index]` is safe. target_index: >- {%- if action_id in action_ids -%} {{ action_ids.index(action_id) }} {%- else -%} -1 {%- endif -%} # Bounds-check the index: action_ids may have more entries than switches. target_entity: >- {%- set idx = target_index | int(-1) -%} {%- if idx >= 0 and idx < (switches | length) -%} {{ switches[idx] }} {%- else -%} {{ none }} {%- endif -%} has_last_interacted_text: "{{ last_interacted_text is string and last_interacted_text | trim != '' }}" # --------------------------------------------------------------------------- # Debug: trigger received # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ is_debug }}" sequence: - service: persistent_notification.create data: notification_id: "mqtt_button_control_debug" title: "MQTT Button Control Debug" message: > Trigger: {{ trigger.id }} Topic: {{ trigger.topic }} action_id: {{ action_id }} action_ids: {{ action_ids }} switches: {{ switches }} target_index: {{ target_index | int(-1) }} target_entity: {{ target_entity if target_entity is not none else 'none' }} has_last_interacted_text: {{ has_last_interacted_text }} - choose: - conditions: - condition: template value_template: "{{ target_entity is not none }}" sequence: - variables: entity_type: "{{ target_entity.split('.')[0] }}" # Persist last interacted entity only when a helper is configured. - choose: - conditions: - condition: template value_template: "{{ has_last_interacted_text }}" sequence: - service: input_text.set_value data: entity_id: "{{ last_interacted_text }}" value: "{{ target_entity }}" - choose: # Light - conditions: - condition: template value_template: "{{ entity_type == 'light' }}" sequence: - variables: timeout_for_indication_blink: !input timeout_for_indication_blink blink_count: !input blink_count blink_timeout: !input blink_interval is_light_on: "{{ is_state(target_entity, 'on') }}" # Inline state access — HA stringifies State objects when # stored in `variables:`, so `.last_changed` must be read # within a single template render. seconds_elapsed: >- {%- set s = states[target_entity] -%} {%- if s is not none -%} {{ (as_timestamp(now()) - as_timestamp(s.last_changed)) | int(0) }} {%- else -%} 0 {%- endif -%} # Blink only when both the idle timeout AND a positive # blink count are configured; otherwise fall through to a # plain toggle (a 0 count would otherwise run an empty # repeat and leave the light untouched). should_blink: >- {{ timeout_for_indication_blink != 0 and (blink_count | int(0)) > 0 and (seconds_elapsed | int(0)) > timeout_for_indication_blink }} # Debug: light branch state - choose: - conditions: - condition: template value_template: "{{ is_debug }}" sequence: - service: persistent_notification.create data: notification_id: "mqtt_button_control_debug_light" title: "MQTT Button Control Debug (light)" message: > target_entity: {{ target_entity }} is_light_on: {{ is_light_on }} seconds_elapsed: {{ seconds_elapsed }} timeout_for_indication_blink: {{ timeout_for_indication_blink }} should_blink: {{ should_blink }} blink_count: {{ blink_count }} blink_timeout (ms): {{ blink_timeout }} - choose: # Blink (indicate light is already on after idle timeout) - conditions: - condition: template value_template: "{{ should_blink and is_light_on }}" sequence: - repeat: count: "{{ blink_count }}" sequence: - service: light.turn_off target: entity_id: "{{ target_entity }}" data: transition: 0 - delay: milliseconds: "{{ blink_timeout }}" - service: light.turn_on target: entity_id: "{{ target_entity }}" data: transition: 0 - delay: milliseconds: "{{ blink_timeout }}" # Actually toggle default: - service: light.toggle target: entity_id: "{{ target_entity }}" # Switch - conditions: - condition: template value_template: "{{ entity_type == 'switch' }}" sequence: - service: switch.toggle target: entity_id: "{{ target_entity }}" # Input Boolean - conditions: - condition: template value_template: "{{ entity_type == 'input_boolean' }}" sequence: - service: input_boolean.toggle target: entity_id: "{{ target_entity }}"