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.

TTSAnnounce
Audio introduction prior to messageNone:
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?NoYes (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
Screengrab of a configuration pop from Lovelace, which to the left of it, has the choice of entities to include in the Lovelace card. To the right of the configuration box, there is a preview of the resulting Lovelace card.
The resulting preview shows that, entity 1 is a switch to turn the automation on and off. The second entity is a text field with the placeholder 'Hello'. The third entity is a select/dropdown box with a list of the locations
This is the resulting Lovelace entities card.

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

Screenshot of the Oxford English Dictionary on the 'pronunciation' page, it shows the symbols that make up the word and how it should be correctly pronunciated.
The Dictionary shows how it should be pronounced 1

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:

Asse, I hope thaz remembered thi pillz

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

  1. Lexico, Meaning of Pronunciation by Lexico, https://www.lexico.com/definition/pronunciation (Accessed 03 April 2020)

Credits

Grandadevans

I am a disabled veteran of 3 tours of Iraq and a tour of Afghanistan as part of the British Army. No longer able to work as I have to lay down on a sofa-bed in my living room 20-ish hours a day. I'm hoping to be able to make a living blogging about my Home Automation /Smart Home journey and maybe regain some dignity in life.

6 thoughts on “Home Assistant Alexa TTS

  • April 4, 2020 at 13:21
    Permalink

    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.

    Reply
    • April 4, 2020 at 21:28
      Permalink

      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 🙂

      Reply
  • April 5, 2020 at 19:13
    Permalink

    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 🙂

    Reply
    • April 5, 2020 at 20:44
      Permalink

      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

      Reply
  • April 6, 2020 at 00:19
    Permalink

    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.

    Reply
    • April 6, 2020 at 06:25
      Permalink

      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 🙂

      Reply

Leave a Reply