Skip to content

Instantly share code, notes, and snippets.

@yougotborked
Last active September 21, 2025 16:43
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.
blueprint:
name: Tuya Zigbee Siren/Doorbell
description: "Set Tuya Zigbee sirens to play a selected melody before activating them."
domain: script
input:
devices:
name: Tuya Zigbee Devices
selector:
device:
integration: zha
multiple: true
melody:
name: Melody
selector:
select:
options:
- Melody 00 (Doorbell Chime)
- Melody 01 (Fur Elise)
- Melody 02 (Westminster Chimes)
- Melody 03 (Fast double door bell)
- Melody 04 (William Tell Overture)
- Melody 05 (Turkish March)
- Melody 06 (Safe/Security Alarm)
- Melody 07 (Chemical Spill Alert)
- Melody 08 (Piercing Alarm Clock)
- Melody 09 (Smoke Alarm)
- Melody 10 (Dog Barking)
- Melody 11 (Police Siren)
- Melody 12 (Doorbell Chime reverb)
- Melody 13 (Mechanical Telephone)
- Melody 14 (Fire/Ambulance)
- Melody 15 (3/1 Elevator)
- Melody 16 (Buzzing Alarm Clock)
- Melody 17 (School Bell)
duration:
name: Duration (seconds)
default: 15
selector:
number:
min: 1
max: 60
step: 1
mode: slider
volume:
name: Volume
selector:
select:
options: [Low, Medium, High]
variables:
devices: !input devices
duration: !input duration
volume_label: !input volume
melody_label: !input melody
melody_num: >
{% set m = melody_label | regex_findall_index('Melody\\s*(\\d+)', 0) %}
{{ '%02d' | format((m | int(0))) }}
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 }}"
# Prefer your observed names
vol_exact: "{{ select_ents | select('search', '_alarm_volume$') | list }}"
mel_exact: "{{ select_ents | select('search', '_alarm_ringtone$') | list }}"
vol_guess: "{{ select_ents | select('search','volume|level') | list }}"
mel_guess: "{{ select_ents | select('search','melody|ringtone|sound|tone|tune|doorbell|music') | list }}"
vol_entity: >
{% set c = (vol_exact if vol_exact|length>0 else vol_guess) %}
{{ c[0] if c|length>0 else '' }}
mel_entity: >
{% set c = (mel_exact if mel_exact|length>0 else mel_guess) %}
{{ c[0] if c|length>0 else '' }}
dur_entity: >
{% set c = number_ents | select('search','duration|time|seconds|length') | 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 '') }}
# ---- Robust volume mapping ----
# Map Low/Medium/High to common option spellings (low/quiet/level 1/etc).
resolved_volume: >
{% if vol_entity %}
{% set opts = (state_attr(vol_entity, 'options') or []) | list %}
{% set want = (volume_label|string)|lower %}
{% set patterns = {
'low': ['\\blow\\b','\\bquiet\\b','\\blevel\\s*1\\b','^1$'],
'medium': ['\\bmedium\\b','\\bmid(dle)?\\b','\\blevel\\s*2\\b','^2$'],
'high': ['\\bhigh\\b','\\bloud\\b','\\blevel\\s*3\\b','^3$']
} %}
{% set pats = patterns.get(want, []) %}
{% set match = '' %}
{% for o in opts %}
{% if not match %}
{% set ol = (o|string)|lower %}
{% for p in pats %}
{% if ol is search(p, ignorecase=True) %}
{% set match = o %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% if not match %}
{# fallback to exact-case tries #}
{% set tries = [volume_label, volume_label|title, volume_label|lower, volume_label|upper] %}
{% for t in tries %}
{% if not match and (t in opts) %}
{% set match = t %}
{% endif %}
{% endfor %}
{% endif %}
{{ match if match else (opts[0] if opts|length>0 else '') }}
{% else %}{{ '' }}{% endif %}
# Melody resolution stays the same
resolved_melody: >
{% if mel_entity %}
{% set opts = state_attr(mel_entity, 'options') or [] %}
{% set exact = 'Melody ' ~ melody_num %}
{% set m = (opts | select('equalto', exact) | list) %}
{% if m|length == 0 %}
{% set m = (opts | select('search', '^Melody\\s*' ~ (melody_num|int)) | list) %}
{% endif %}
{% if m|length == 0 %}
{% set m = (opts | select('search', melody_num) | list) %}
{% endif %}
{{ (m[0] if m|length>0 else (opts[0] if opts|length>0 else '')) }}
{% else %}{{ '' }}{% endif %}
# Duration (optional)
- choose:
- conditions: "{{ dur_entity|length > 0 }}"
sequence:
- service: number.set_value
target: { entity_id: "{{ dur_entity }}" }
data: { value: "{{ duration | int }}" }
# ----- ORDER & tiny delays help many Tuya sirens -----
# 1) Volume first
- 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.50"
# 2) Melody second
- choose:
- conditions: "{{ mel_entity|length > 0 and resolved_melody|length > 0 }}"
sequence:
- service: select.select_option
target: { entity_id: "{{ mel_entity }}" }
data: { option: "{{ resolved_melody }}" }
- delay: "00:00:00.30"
# 3) 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