Files
haos-blueprints/Zigbee/MQTT Button Control/blueprint.yaml
T
alexei.dolgolyov 3d15f481a0 feat: add debug mode and clarify docs in MQTT Button Control
- Add optional debug notifications for trigger and light-branch decisions
- Document index-based action/entity mapping and two-topic support
- Add configuration reference table and debug-mode section to README
2026-05-27 13:09:59 +03:00

316 lines
11 KiB
YAML

# 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 }}"