Skip to content

Instantly share code, notes, and snippets.

@yougotborked
Last active December 25, 2025 18:06
Show Gist options
  • Select an option

  • Save yougotborked/5f3270e8fc6da9f82f72832cc48946a1 to your computer and use it in GitHub Desktop.

Select an option

Save yougotborked/5f3270e8fc6da9f82f72832cc48946a1 to your computer and use it in GitHub Desktop.
# Tuya Zigbee Siren/Doorbell (ZHA) — Use the *existing HA entities* (select/number/siren/switch)
#
# This version does NOT write raw Zigbee datapoints.
# It uses the same entity interfaces you see on the device page:
# - select.<device>_alarm_volume
# - select.<device>_alarm_ringtone
# - number.<device>_alarm_duration
# - siren.<device> (or switch.<device>)
#
# Why your original blueprint likely failed:
# - The template used `is search(p, ignorecase=True)` which HA’s template test doesn’t support.
# That can cause template rendering errors and prevent the select from being called.
# - Even when it renders, option labels often don’t match `Low/Medium/High` exactly.
#
# How to handle “wrong ringtone names/order” while still using the UI entities:
# - Create an `input_select` helper and copy/paste the *exact* options from your device’s
# select.<device>_alarm_ringtone attributes.
# - In this blueprint, you pick that helper; the script forwards the helper’s current value
# to the device select entity. This way you’re selecting the same “item” you would pick
# manually, even if the labels are nonsense.
blueprint:
name: Tuya Zigbee Siren/Doorbell (Entity Interface)
description: "Set Tuya Zigbee siren volume/ringtone/duration using the existing ZHA entities, then activate."
domain: script
input:
devices:
name: Tuya Zigbee Devices (ZHA)
selector:
device:
integration: zha
multiple: true
# Optional: drive ringtone selection from a helper so you can use the *device’s own* option labels.
ringtone_helper:
name: Ringtone helper (input_select)
description: "Optional. Pick an input_select whose options match the siren's ringtone list. The helper's state is forwarded to select.*_alarm_ringtone."
default: ""
selector:
entity:
domain: input_select
# Optional: same idea for volume (only needed if the device's volume labels aren't Low/Medium/High).
volume_helper:
name: Volume helper (input_select)
description: "Optional. If set, forwards helper state to select.*_alarm_volume. Otherwise maps Low/Medium/High to the device options by position (0/1/2) or label match."
default: ""
selector:
entity:
domain: input_select
melody:
name: Ringtone choice (fallback)
description: "Used only if ringtone_helper is not provided. Must match an option on the device (exact text)."
default: ""
selector:
text: {}
volume:
name: Volume (fallback)
description: "Used only if volume_helper is not provided."
default: Medium
selector:
select:
options: [Low, Medium, High]
duration:
name: Duration (seconds)
default: 15
selector:
number:
min: 1
max: 180
step: 1
mode: slider
pre_stop:
name: Turn off before configuring
description: "Some Tuya sirens ignore config writes while alarming."
default: true
selector:
boolean: {}
variables:
devices: !input devices
duration_s: !input duration
volume_label: !input volume
ringtone_fallback: !input melody
ringtone_helper: !input ringtone_helper
volume_helper: !input volume_helper
pre_stop: !input pre_stop
sequence:
- repeat:
for_each: "{{ devices | list }}"
sequence:
- variables:
ents: "{{ device_entities(repeat.item) | list }}"
number_ents: "{{ ents | select('search','^number\\.') | list }}"
select_ents: "{{ ents | select('search','^select\\.') | list }}"
switch_ents: "{{ ents | select('search','^switch\\.') | list }}"
siren_ents: "{{ ents | select('search','^siren\\.') | list }}"
vol_entity: >
{% set c = select_ents | select('search', '_alarm_volume$') | list %}
{{ c[0] if c|length>0 else '' }}
ring_entity: >
{% set c = select_ents | select('search', '_alarm_ringtone$') | list %}
{{ c[0] if c|length>0 else '' }}
dur_entity: >
{% set c = number_ents | select('search', '_alarm_duration$') | list %}
{{ c[0] if c|length>0 else '' }}
siren_entity: "{{ siren_ents[0] if siren_ents|length>0 else '' }}"
sw_entity: >
{% set pref = switch_ents | select('search','siren|alarm|doorbell|buzzer') | list %}
{{ pref[0] if pref|length>0 else (switch_ents[0] if switch_ents|length>0 else '') }}
# Resolve volume option:
# 1) If volume_helper provided, use its state verbatim.
# 2) Else try label match (case-insensitive).
# 3) Else fall back by index (Low=0, Medium=1, High=2).
resolved_volume: >
{% if not vol_entity %}{{ '' }}
{% else %}
{% set opts = (state_attr(vol_entity, 'options') or []) | list %}
{% if volume_helper %}
{{ states(volume_helper) }}
{% else %}
{% set want = (volume_label|string)|lower %}
{% set match = '' %}
{% for o in opts %}
{% if not match and ((o|string)|lower == want) %}
{% set match = o %}
{% endif %}
{% endfor %}
{% if not match %}
{% set idx = {'low':0,'medium':1,'high':2}.get(want, 1) %}
{{ opts[idx] if opts|length>idx else (opts[0] if opts|length>0 else '') }}
{% else %}
{{ match }}
{% endif %}
{% endif %}
{% endif %}
# Resolve ringtone option:
# 1) If ringtone_helper provided, use its state verbatim.
# 2) Else use the free-text fallback (must exactly match an option).
resolved_ringtone: >
{% if not ring_entity %}{{ '' }}
{% else %}
{% if ringtone_helper %}
{{ states(ringtone_helper) }}
{% else %}
{{ ringtone_fallback }}
{% endif %}
{% endif %}
# Optional: stop first (helps when the device ignores config writes while active)
- choose:
- conditions: "{{ pre_stop }}"
sequence:
- choose:
- conditions: "{{ siren_entity|length > 0 }}"
sequence:
- service: siren.turn_off
target: { entity_id: "{{ siren_entity }}" }
- conditions: "{{ siren_entity|length == 0 and sw_entity|length > 0 }}"
sequence:
- service: switch.turn_off
target: { entity_id: "{{ sw_entity }}" }
- delay: "00:00:00.30"
# Duration
- choose:
- conditions: "{{ dur_entity|length > 0 }}"
sequence:
- service: number.set_value
target: { entity_id: "{{ dur_entity }}" }
data:
value: "{{ duration_s | float }}"
- delay: "00:00:00.20"
# Volume
- choose:
- conditions: "{{ vol_entity|length > 0 and resolved_volume|length > 0 }}"
sequence:
- service: select.select_option
target: { entity_id: "{{ vol_entity }}" }
data:
option: "{{ resolved_volume }}"
- delay: "00:00:00.20"
# Ringtone
- choose:
- conditions: "{{ ring_entity|length > 0 and resolved_ringtone|length > 0 }}"
sequence:
- service: select.select_option
target: { entity_id: "{{ ring_entity }}" }
data:
option: "{{ resolved_ringtone }}"
- delay: "00:00:00.20"
# Activate
- choose:
- conditions: "{{ siren_entity|length > 0 }}"
sequence:
- service: siren.turn_on
target: { entity_id: "{{ siren_entity }}" }
- conditions: "{{ siren_entity|length == 0 and sw_entity|length > 0 }}"
sequence:
- service: switch.turn_on
target: { entity_id: "{{ sw_entity }}" }
mode: parallel
max_exceeded: silent
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment