Skip to content

Instantly share code, notes, and snippets.

@cmsj
Last active August 13, 2025 07:55
Show Gist options
  • Save cmsj/85eedaf1f1f4d0dc8c3d31fe5ebfa7c1 to your computer and use it in GitHub Desktop.
Save cmsj/85eedaf1f1f4d0dc8c3d31fe5ebfa7c1 to your computer and use it in GitHub Desktop.
alias: Universal TTS Announcement 2
description: Sends a TTS message, routing to Alexa or Cloud TTS based on the target entity.
icon: mdi:bullhorn-variant-outline
fields:
message:
name: Message
description: The text you want to announce.
required: true
selector:
text:
multiline: true
target:
name: Target
description: Select any combination of media player entities, areas, devices, or labels.
required: true
selector:
target:
entity:
domain: media_player
volume:
name: (Optional) Announcement Volume
description: Set a temporary volume for the announcement (0.0 to 1.0).
selector:
number:
min: 0
max: 1
step: 0.05
mode: slider
sequence:
- variables:
expanded_targets: >
{# Create the namespace object we'll use to store all our working data #}
{% set data = namespace(expanded_targets=[]) %}
{# Declare two helper macros #}
{% macro m_listifyIfNot(someValue, returns) %}
{% if someValue is list %}
{% do returns(someValue) %}
{% else %}
{% do returns([someValue]) %}
{% endif %}
{% endmacro %}
{% macro m_noop(someValue, returns) %}
{% do returns(someValue) %}
{% endmacro %}
{# Declare our helper macros as functions, so they can return values #}
{% set f_listifyIfNot = m_listifyIfNot | as_function %}
{% set f_noop = m_noop | as_function %}
{# Iterate our input dictionary #}
{% for value in target %}
{# Store a function pointer we'll use to turn the input value into a list of entities #}
{% if value == "device_id" %}
{% set f_getEntities = device_entities %}
{% elif value == "area_id" %}
{% set f_getEntities = area_entities %}
{% elif value == "label_id" %}
{% set f_getEntities = label_entities %}
{% elif value == "entity_id" %}
{% set f_getEntities = f_noop %}
{% else %}
{# This should never be hit #}
{% set f_getEntities = none %}
{% endif %}
{# Ensure the input value is a list. It will be a string if only one value is supplied #}
{% set listifiedValues = f_listifyIfNot(target[value]) %}
{# Iterate the input list and use our function pointer to expand it to a list of entities, and filter out anything that isn't a media_player #}
{% for entry in listifiedValues %}
{% set data.expanded_targets = data.expanded_targets + expand(f_getEntities(entry)) | map(attribute="entity_id") | select("match", "media_player") | list %}
{% endfor %}
{% endfor %}
{{ data.expanded_targets | unique | list }}
- variables:
all_alexa_entities: "{{ integration_entities('alexa_media') }}"
alexa_targets: |
{{ expanded_targets | select('in', all_alexa_entities) | list }}
media_player_targets: |
{{ expanded_targets | reject('in', all_alexa_entities) | list }}
- if:
- condition: template
value_template: "{{ volume is defined and volume > 0 }}"
then:
- target:
entity_id: "{{ expanded_targets }}"
data:
volume_level: "{{ volume }}"
action: media_player.volume_set
- delay:
milliseconds: 250
- if:
- condition: template
value_template: "{{ alexa_targets | count > 0 }}"
then:
- target:
entity_id: "{{ alexa_targets }}"
data:
message: "{{ message }}"
data:
type: tts
action: notify.alexa_media
- if:
- condition: template
value_template: "{{ media_player_targets | count > 0 }}"
then:
- target:
entity_id: "{{ media_player_targets }}"
data:
message: "{{ message }}"
action: tts.cloud_say
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment