I am sharing with you a “service” I created to simplify randomized TTS messages preceded by an optional chime sound. The goal of my service is to consolidate all of the common setup for playing an optional chime, then an optional random (or static, or templated) TTS message so that I can use it throughout my normal automations easily and consistently. A nice advantage to having this centralized is that I won’t have variations of the chime + TTS pattern and “tts.microsoft_say” littered through my other automations. This has been tested on HASSIO version 0.111.4.
I have the word “service” in quotes because while getting it working I learned about a workaround to a limitation with scripts in that they can’t currently run more than once at a time, resulting in an error in the logs to the effect of “script already running”. The workaround is to use a custom event to trigger an automation. I have heard that there is a pull-request in the works to fix the parallel script handling, but for now this workaround is operating well.
First, let’s look at how this is called from an automation:
#in an automation:
action:
- event: chime_tts
event_data:
entity_id: media_player.bmod_keith_tablet
sound: airplane-cabin-ding.mp3
delay: 2000
random: [
"Keith is awesome!",
"Let's get started!",
"You're here!",
"I've been waiting for you to arrive!"
]
The event
value is our custom event chime_tts
.
The event_data
has the values needed by our chime_tts
automation. If you want to use templating to define the values, use event_data_template
. You could use templating to select a specific sound conditionally, or to adapt phrases based on what triggered your automation, among other things.
The required entity_id
can be any valid media_player entity, or “all”.
The optional sound
value would be a sound file you place in /config/www/sounds
, which incidentally is available at http://your-homeassistant-url:8123/local/sounds
.
The optional delay
value is how many milliseconds to wait for the sound to play before playing the TTS so that the TTS doesn’t play before the sound is finished; default 2000.
The optional random
value is an array of phrases to say.
If you just want to play a single TTS phrase, use the optional message
property instead of random
. If you provide both, random
takes precedence over message
Here is an example calling with just a single message using templating:
action:
- event: chime_tts
event_data_template:
entity_id: all
sound: airplane-cabin-ding-dong.mp3
message: "We don't live in a barn. Close the {{ trigger.to_state.name }} door."
Here is an example with random phrases using templating:
action:
- event: chime_tts
event_data_template:
entity_id: media_player.bmod_keith_tablet
random: [
"I'll get the lights for you. {{ ['this time', 'spoiled sap', 'lazy bum'] | random" }},
"I guess you aren't coming back. I'll get the lights.",
"Hello? I guess everyone left me alone. I might as well make it dark too."
]
Now, let’s look at the “service” automation:
- id: chime_tts
alias: Chime + TTS
trigger:
- platform: event
event_type: chime_tts
action:
- service: media_player.play_media
data_template:
entity_id: "{{ trigger.event.data.entity_id }}"
media_content_id: "{{ base_url }}/local/sounds/{{ trigger.event.data.sound }}"
media_content_type: music
- delay:
milliseconds: "{{ trigger.event.data.delay | default(2000 if trigger.event.data.sound else 0) }}"
- service: tts.microsoft_say
data_template:
entity_id: "{{ trigger.event.data.entity_id }}"
message: "{{ trigger.event.data.random|random if trigger.event.data.random else trigger.event.data.message }}"
- the
trigger
is watching for our custom eventchime_tts
- first
action
plays the sound, if given - second
action
waits for the sound to finish playing, or waits 0 time if sound wasn’t given - third
action
plays a random TTS phrase fromrandom
if given, or the singlemessage
if given
Notable behavior:
- If this event is called again while it’s already active you will not hear everything played. The last call to play aborts the first call and plays. This is expected due to how there isn’t any queueing, but I would love to have smart queue/clobber handling if I can figure out a way to make it work. For example, if a door is opened and closed again before the first initiated sequence is finished playing then I’d like to hear the initial chime and the 2nd TTS can clobber the first TTS, but if two different doors are opened at the same time I’d like to hear the first chime, then the first TTS, then the second TTS without the second chime (since the chime is to get attention, it doesn’t need to be played again in that scenario).
Future improvements:
-
Add handling for scheduled quiet hours and temporary muting.
-
The first and third actions really need to be conditionally executed rather than always executed. They effectively don’t do anything if the sound or message aren’t given, but they are still called and left to their own handling of the missing values. I haven’t figured out how to conditionally skip an action yet.
-
The delay you need to specify needs to be based on the duration of the sound, which may need to be tuned per sound effect. I would love to figure out how to automatically wait until the sound finishes playing or detect the duration from the file.
-
Add optional push notifications via SMS and/or mobile app notifications so this can be the single notification service wrapper used by all my automations. I already have these working separately, so this should be an easy thing to add.
-
Find a way to implement a smart queue/clobber handler, as mentioned above in the “notable behavior” section.