Home Assistant Alexa TTS
Note: Please, be aware that this post may contain affiliate links.
Did you know that you can integrate your Amazon Alexa with Home Assistant for free and gain a lot of extra functionality? This includes the subject of this post – Text to Speech; or TTS for short.
We can achieve this using the fantastic custom component “Alexa Media Player“
About Text to Speech (TTS)
With the TTS Functionality, you can get free readings of basically, whatever you want. For example, I use a single TTS Flow/tab in NodeRED in which I pass the output of around 10 other flows/tabs with TTS notification/announcements on anything from
“Don’t forget to pay the Milkman”
Said every Thursday at 21:45
to
“Rowan’s light has been turned on – but don’t worry, as it’s past his lights out time, I’ve turned it back off again”
When our Son turns his light on after light-out
You can select the device on which the text is announced, or even select a group of devices (as long as you have the group already set up in the Alexa app)
What about “announce“
As well as the TTS option for the notify component, we can also choose to use the announce component.
The differences in the 2 are minor but worth exploring, and sometimes, one method is preferable over the other.
TTS | Announce | |
Audio introduction prior to message | None: The sent message is read straight out on the chosen entities | Ding Dong: There is a ding dong sound before any messages are played. (this doesn’t appear to be the case when I play announcements via Node-RED though) |
SSML available? | No | Yes (section below dedicated to it) |
Setup
A quick caveat: Here, all the files I mention have a basepath of /config
. This may not be the same on your system. To give you a reference point, my main Home Assistant configuration file is at /config/configuration.yaml
As with the Home Assistant Attributes Card, you may be better off installing HACS, if you don’t already have it installed, It’s easy to do, and something I hope to be covering in the future.
As a bonus, if you set up using HACS, you can simply add the Amazon Echo to your system using the Integrations panel, and not bother with editing the config file.
In order to add it through HACS, go to the Integration tab in your Configuration panel and search for “Alexa Media Player”. Then go into it and install it.
You will then need to enter your Amazon credentials, along with any 2FA details.
Home Assistant Package
If you’re not familiar with Home Assistant Packages – they allow you to group all your activity based around one feature/integration into a single file. So for this use case, we can group all of our Alexa code and keep it all together. This makes it much neater and more manageable, in my opinion.
First, let’s set up our packages file (if you’re using packages) create our Alexa file in the packages directory /code/packages/alexa.yaml
.
Tell Home Assistant we want to “notify”
We need to tell Home Assistant that we wish it to plug into the alexa_media notify system. We can do that by entering the below code as our first code in the package file (or another place in your configuration).
notify:
- platform: alexa_media
name: alexa_media
The Service to use for Home Assistant TTS
You can get the TTS functionality by calling the notify.alexa_media
service. Alternatively, you could call notify.alexa_media_
followed by the entity that you want to send the notification to. For example, notify.alexa_media_kitchen
.
Note above that I had to enter media_player.kitchen if I used the notify.alexa_media
service, but just notify.alexa_media_kitchen
(without the domain media_player) if I added the entity to the service call.
Passing Data in to the Service
To give you an example: If you were to call the service from the Developer Tools panel, you would need to pass in an object such as this if you were using JSON:
{
"message": "hello",
"data": {
"type": "tts"
},
"target": [
"media_player.kitchen", # Device ID
"group.downstairs", # Group ID
"Living Room", # Room Name
"1234567890ABCDE" # Device Serial Number
]
}
…or if you are entering data using YAML, for example through the Developer Tools page…
message: hello
data:
type: tts
target:
- kitchen
- group.downstairs
- Living Room
- 12345678790ABCDE
Example Automations
This first example is a script that will read something like the below out in the morning.
Good Morning, John. The temperature inside is currently 22.0 degrees, and the heating is currently set to 20.0 degrees. The temperature outside is currently 7.35 degrees.
script:
john_woke_up:
sequence:
- service: notify.alexa_media
data_template:
data:
type: tts
target:
- media_player.downstairs
message: '
Good Morning, John.
The temperature inside is currently {{ states("sensor.living_room_current_temperature") }} degrees, and the heating is currently
{% if is_state("climate.living_room", "off") -%}
turned off.
{%- else -%}
set to {{ states("sensor.living_room_temperature_3") }} degrees.
{%- endif %}
The temperature outside is currently {{ states("sensor.environment_outside_temperature") }} degrees.'
The above could be triggered by motion sensor or via an Alexa app Routine etc.
The next one will remind you every 5 minutes if you have a timer on that is over 5 minutes.
automation:
- id: timer_announcement
alias: Timer Announcement
trigger:
- platform: template
value_template: >
{%- if states.sensor.kitchen_next_timer.state != "unavailable" -%}
{%- set sorted_active = states.sensor.kitchen_next_timer.attributes.sorted_active | from_json -%}
{%- set duration = (sorted_active[0][1].remainingTime / 60000) | round(0) -%}
{%- set remaining = (((as_timestamp(states.sensor.kitchen_next_timer.state) - as_timestamp(states.sensor.time.last_changed)) / 60) | int) -%}
{{ duration > 5 and remaining != duration and remaining != 0 and remaining % 5 == 0 }}
{%- endif -%}
action:
- service_template: >-
notify.alexa_media_kitchen
data_template:
message: >-
{%- if states.sensor.kitchen_next_timer.state != "unavailable" -%}
{%- set sorted_active = states.sensor.kitchen_next_timer.attributes.sorted_active | from_json -%}
{%- set duration = (sorted_active[0][1].remainingTime / 60000) | round(0) -%}
{%- set remaining = (((as_timestamp(states.sensor.kitchen_next_timer.state) - as_timestamp(now())) / 60) | int) -%}
You have {{ remaining }} {{ "minutes" if duration > 1 else "minute" }} left on your {{ duration }} minute timer.
{%- endif -%}
data:
type: announce
method: all
This one would need altering a little to take account of your own personal service and entity IDs.
The next example will give you a:
- text input field for you to enter text to be read out
- select box with a list of your Amazon Echo devices
- switch the will allow you to turn the automation off
input_text:
alexa_tts:
name: Alexa TTS
initial: Hello
input_select:
alexa:
name: Alexa
options:
- None
- Dad
- Kitchen
- Downstairs
- Rowan
- Announce!
initial: None
icon: mdi:target
automation:
- id: alexaTTSFromForumThread
alias: Alexa TTS
trigger:
platform: state
entity_id: input_select.alexa
action:
- service: notify.alexa_media
data_template:
data:
type: tts
target: >
{% if is_state('input_select.alexa', 'Living Room') %}
media_player.dad
{% elif is_state('input_select.alexa', 'Downstairs') %}
group.downstairs
{% elif is_state('input_select.alexa', 'Bedroom') %}
media_player.upstairs
{% elif is_state('input_select.alexa', 'Kitchen') %}
media_player.kitchen
{% elif is_state('input_select.alexa', 'Announce!') %}
group.everywhere
{% elif is_state('input_select.alexa', 'None') %}
false
{% else %}
false
{% endif %}
message: "{{states.input_text.alexa_tts.state }}"
- delay: '00:00:02'
- service: input_select.select_option
data:
entity_id: input_select.alexa
option: None

SSML
Apart from the default, which is for Alexa to read all the text verbatim, she can also read out text SSML; or Speech Synthesis Markup Language.
What is SSML

What is SSML? I hear you ask. Well, instead of reading the text as Amazon’s AI thinks you should hear it, you can tell it how you want it to pronounce things.
It’s done using the same funny symbols they use to tell you how to pronounce words in a dictionary.
The best resource I have found for SSML is in actuality on the Amazon site.
SSML Example
Here’s something I got together to replace me daily medication alerts. Instead of the normal “John, It’s time to take x; y & z medication” that I’ve been getting 4 times a day, I’ve instructed her to say…
<speak>
<phoneme alphabet="ipa" ph="æ.sE">Excuse me</phoneme>, I
<phoneme alphabet="ipa" ph="oʊ'p">hope</phoneme>
<phoneme alphabet="ipa" ph="θæz">you have</phoneme> remembered
<phoneme alphabet="ipa" ph="θI">your</phoneme>
<phoneme alphabet="ipa" ph="'pIlz">pills</phoneme>
</speak>
You can see in the tags, what they are supposed to say: you are just telling Alexa how it should be pronounced.
The audio written above, sounds like this:
Have there been times when…
If you have a voice assistant, I bet there have been times that you’ve thought “If only I could tell her to say this and ask her to pronounce it in a certain way. For example, in Barnsley, UK, where I live, and many other places, the word “I” is pronounced as a hard “a”, like the a in trap. Well that is what SSML allows you to do. In this example, the hard a in trap is written “æ”. Now you can see that no matter what you want to say, you can say it with a bit of work.
As well as telling her how to pronounce things, here are just a few of the things you can tell her to do with the speech she outputs:
Other SSML Tricks
<amazon:domain>
– style of reading<amazon:effect>
– whisper<amazon:emotion>
– exited or disappointed<audio>
– play an existing audio file<break>
– add a variable-length break<emphasis>
– change rate and volume<lang>
– change the language of the output<p>
– represents a paragraph<phoneme>
– instructions on pronunciation<prosody>
– volume, rate & pitch of the tagged output<s>
– represents a sentence<say-as>
– instructions on how to say the tagged content, eg as numbers, spell each letter out, bleep out the word<voice>
– change the voice from one ‘person‘ to one of many others
For further information on SSML, I recommend you check out the Amazon SSML Documentation
Our Final Package File
So the last thing to do, apart from the references and credits, is to show you the final package file that contains all of the things that I have included.
notify:
- platform: alexa_media
name: alexa_media
script:
# Set this off from an Alexa Routine, or something similar.
# Maybe, after you say "Alexa, Good Morning"
john_woke_up:
sequence:
- service: notify.alexa_media
data_template:
data:
type: tts
target:
- media_player.dad
- media_player.kitchen
message: '
Good Morning, John.
The temperature inside is currently {{ states("sensor.living_room_current_temperature") }} degrees, and the heating is currently
{% if is_state("climate.living_room", "off") -%}
turned off.
{%- else -%}
set to {{ states("sensor.living_room_temperature_3") }} degrees.
{%- endif %}
The temperature outside is currently {{ states("sensor.environment_outside_temperature") }} degrees.'
input_text:
alexa_tts:
name: Alexa TTS
initial: Hello
input_select:
alexa:
name: Alexa
options:
- None
- Dad
- Kitchen
- Downstairs
- Rowan
- Announce
initial: None
icon: mdi:target
automation:
- id: alexaTTSFromForumThread
alias: Alexa TTS
trigger:
platform: state
entity_id: input_select.alexa
action:
- service: notify.alexa_media
data_template:
data:
type: tts
target: >
{% if is_state('input_select.alexa', 'Living Room') %}
media_player.dad
{% elif is_state('input_select.alexa', 'Downstairs') %}
group.downstairs
{% elif is_state('input_select.alexa', 'Bedroom') %}
media_player.upstairs
{% elif is_state('input_select.alexa', 'Kitchen') %}
media_player.kitchen
{% elif is_state('input_select.alexa', 'Announce') %}
group.everywhere
{% elif is_state('input_select.alexa', 'None') %}
false
{% else %}
false
{% endif %}
message: "{{states.input_text.alexa_tts.state }}"
- delay: '00:00:02'
- service: input_select.select_option
data:
entity_id: input_select.alexa
option: None
- id: timer_announcement
alias: Timer Announcement
trigger:
- platform: template
value_template: >
{%- if states.sensor.kitchen_next_timer.state != "unavailable" -%}
{%- set sorted_active = states.sensor.kitchen_next_timer.attributes.sorted_active | from_json -%}
{%- set duration = (sorted_active[0][1].remainingTime / 60000) | round(0) -%}
{%- set remaining = (((as_timestamp(states.sensor.kitchen_next_timer.state) - as_timestamp(states.sensor.time.last_changed)) / 60) | int) -%}
{{ duration > 5 and remaining != duration and remaining != 0 and remaining % 5 == 0 }}
{%- endif -%}
action:
- service_template: >-
notify.alexa_media_kitchen
data_template:
message: >-
{%- if states.sensor.kitchen_next_timer.state != "unavailable" -%}
{%- set sorted_active = states.sensor.kitchen_next_timer.attributes.sorted_active | from_json -%}
{%- set duration = (sorted_active[0][1].remainingTime / 60000) | round(0) -%}
{%- set remaining = (((as_timestamp(states.sensor.kitchen_next_timer.state) - as_timestamp(now())) / 60) | int) -%}
You have {{ remaining }} {{ "minutes" if duration > 1 else "minute" }} left on your {{ duration }} minute timer.
{%- endif -%}
data:
type: announce
method: all
References
- Lexico, Meaning of Pronunciation by Lexico, https://www.lexico.com/definition/pronunciation (Accessed 03 April 2020)
Credits
- Featured Image is by Lazar Gugleta on Unsplash
Lazar Gugleta
Thank you for your service and for this, you put a lot of work into it and it’s appreciated. Keep up the good work there HA community needs people like you!! Thank you again.
Hey Juan,
Thank you very much for your beautiful comment. It really makes a difference to know that all the work I put in is appreciated. Especially at this stage of the website lifecycle. Currently, it doesn’t have much content on it, so not many people stay for more than one page. It will get better when there is a lot more content here and people can both browse and there’s a large amount of resource material available.
Again, thanks for the brilliant comment – I will try not to disappoint 🙂
Hey Mr Hasscasts, a superb article with plenty of detail. I love your choice of code highlighter plugin! A couple of your Youtube videos really helped me out when I was just starting out with Home Assistant, so thanks and keep up the good work 🙂
Hi Siytek,
Thank you so much for your kind words. I tried my normal approach of default code blocks. But as a ex Software Engineer, it looks both bare and feels counter-intuitive. There are a few things that I found after I installed the plugin ie it doesn’t support SSML, but a different language did just as well 🙂
Thanks also for watching the YouTube videos.
I will try to keep up the good work… well as best I can anyway 🙂
Thanks again,
John
Hi John,
I must admit that I had unsubscribed from your videos as I wasn’t enjoying them much but boy have you found your calling! Your blog posts are well written, detailed, extremely well formatted and overall just a very nice read. I’ve subscribed to these using Feedly and look forward to your next one. Keep up the good work and stay safe.
Thank you very much for your honesty, I must admit that I have to agree with you RE the YouTube videos, it was at a point where I no longer enjoyed making them. What with the software issue I had and the fact that all the decent videos I did took at least 4 full takes to get things right, then I still wasn’t satisfied.
It just wasn’t worth it.
I enjoy Blogging much more. I just need to get a good catalogue of content up so that people stay around for longer and the Search Engines send way more traffic.
My main concerns now are more about what content to cover.
Right, thank you again but I have to answer your other comment now. Obviously, I’d rather have two many comments than none 🙂