SONOS TTS Script

Hi all,

the new text-to-speech functionality is great, but using it on SONOS can be a little bit annoying, since the Speaker always “remembers” the last played media. So if you say something e.g in the kitchen over TTS and then go back to the kicthen and press play on SONOS to listen to music witch was load previously, it plays your TTS string again instead of the music you expected.

The good thing: The SONOS componente has snapshot and restore functions for issues like this.
The bad thing: I don’t want to blow up my automation actions with snapshots and restores, since its allways the same.

Therefore I created a small but flexible and helpful “sonos tts” script using template variables:

script:
# > SONOS TTS
   say:
     alias: SONOS TTS
     sequence:
       - service: media_player.sonos_snapshot
         data_template:
           entity_id: "{{ where }}"
       - service: tts.google_say
         data_template:
           entity_id: "{{ where }}"
           message: "{{ what }}"
       # Add a delay in a length which you think fits best for all the things you want to say over TTS
       - delay: '00:00:07'
       - service: media_player.sonos_restore
         data_template:
           entity_id: "{{ where }}"

Automations using TTS over SONOS now only need to call the fowwling action:

 - service: script.turn_on
   entity_id: script.say
   data:
     variables:
       where: 'media_player.livingroom'
       what: 'Hi there!'

Just wanted to share it. Maybe someone else might find that helpful too.

Cheers,

Dominik

UPDATE: See the update below for a new version with dynamically calculated delay based on the media length.

29 Likes

Thank you so much

1 Like

I gave up on Sonos TTS because I had trouble with snapshots and speakers in groups. Will give this a try. Thanks.

I think the line “data:” should be removed. This wouldn’t work until I removed it. My working copy looks like this

- service: tts.google_say
  data_template:
    entity_id: "{{ where }}"
    message: "{{ what }}"

Thank you very much. This is a much better way for me to use TTS.

True! Copy&Paste error caused by struggling with the preformated text here. I’ll edit my post. THNAKS! :slight_smile:

I am not having any luck with the sonos_restore command. It plays the text on my Sonos speaker, but does not resume playing music. I’ve tried it with My Library music playing and with Pandora. Any ideas?

#configuration.yaml
tts:
- platform: google

#scripts
say_my_weather:
alias: Read My Weather
sequence:
- service: media_player.sonos_snapshot
data_template:
entity_id: “{{ where }}”
- service: tts.google_say
data_template:
entity_id: “{{ where }}”
message: “{{ what }}”
- service: media_player.sonos_restore
data_template:
entity_id: “{{ where }}”

my_weather:
  alias: My Weather
  sequence:
    - service: script.turn_on
      entity_id: script.say_my_weather
      data:
        variables:
          where: "media_player.living_room"
          what: "Hello, the wWeather at Home is"

Is it playing the same song/radio/whatever when you press play again after the restore?

It still has the sound byte of the text in the now playing. When you press play it replays the TTS audio.

1 Like

Hmmm. Strange. I have no issues with the snapshot and restore. Are you on the latest version of HA and you Sonos setup in HA is working correctly?

@domlen how about adding this the main ‘say’ service? :slight_smile:

1 Like

Hello!

I found that the restore will run, even if the tts is still playing and the output will be cut off.
This is is how i solved it.

alias: SONOS TTS
sequence:
  - service: media_player.sonos_snapshot
    data_template:
      entity_id: "media_player.{{ where }}"
  - service: tts.google_say
    data_template:
      entity_id: "media_player.{{ where }}"
      message: "{{ message }}"
  - delay: '00:00:{{ states.media_player[where].attributes.media_duration | int }}'
  - service: media_player.sonos_restore
    data_template:
      entity_id: "media_player.{{ where }}"

Unfortunately the delay only works for me when being specified within the 00:00:00 notation and not in seconds: {{ code }}. Because of that it will obviously only work for durations less than 60 seconds :frowning:

Cheers Hannes

3 Likes

@cyberkov Awesome, thanks! I had the anticipation that this might happen, but did not run into into that issue yet and had no idea how to solve it.

@domlen After I investigated some more I found I was getting a lot of locked database lines in the log. I even found that when I stopped fiddling with the system after 4-5 minutes it did restore the music to the Sonos. I’m assuming this is related to the database??

Why doesn’t HA install with a better database or have information in the getting started to setup a better database? I’ve found a few links in the forum on setting up a MySQL DB for HA and I am going to go and try that. If that doesn’t work I’ll attempt cyberkov’s method that he posted below.

If i understand the code correctly the database is not involved in the snapshot process because it is just using a mechanism of the underlying SoCo library. It seems to be kept in memory. But unfortunately I do not speak python neither do I have enough knowledge about the hass internals to be 100% sure about it. :wink:

Thanks guys for amaizing job.
I am having a problem regarding this line:
- delay: '00:00:{{ states.media_player[where].attributes.media_duration | int }}'
After the TTS message the Sonos stops and on pressing Play it replays TTS message.
My message is 2 seconds long. I am afraid that the delay line would be incorrect for
messages under 10 seconds, because it will be, in my case : delay: '00:00:2'
I tried a 0 (zero) in front of {{ states.media_player …, but it does not work.
But, if I am setting delay: '00:00:05' it works as intended.
Is anyone else having the same issue ?

Thanks!

Hi, I have not tested it in the script yet, only in the dev-template view but I guess it should work. Please try:

- delay: '00:00:{{ ('0' ~ states.[where].attributes.media_duration) | reverse | truncate(2, True, '') | reverse }}'

What ist does, example 1 duration = 23, example 1 duration = 5

  • It ads a 0 to the to the beginning of the duration, so we get 023 (example 1) or 05 (Exemplare 2)
  • it reverses it to 320 or 50
  • it truncates it to two digits from right, so we get 32 or 50. (This is why the reverse is needed, since there is no truncate from left)
  • it reverses back so we finaly have 23 or 05
  • which then can be added to the duration 00:00:23 or 00:00:05

But again, also this only works if the duration is <=59 seconds!

1 Like

Ok, I tested my idea from above right now and while the template is valid, the script failed to be parsed :frowning2:

- delay: '00:00:{{ ('0' ~ states.[where].attributes.media_duration) | reverse | truncate(2, True, '') | reverse }}'

Brings up:

17-01-16 10:23:34 ERROR (Thread-1) [homeassistant.util.yaml] while parsing a block mapping
in "/home/hass/.homeassistant/scripts/sonos_tts.yaml", line 26, column 9
expected <block end>, but found '<scalar>'
in "/home/hass/.homeassistant/scripts/sonos_tts.yaml", line 26, column 28

and then I tested (double quotes outside and singe quotes inside of the template)

- delay: "00:00:{{ ('0' ~ states.[where].attributes.media_duration) | reverse | truncate(2, True, '') | reverse }}"

which endet in

17-01-16 10:24:41 ERROR (MainThread) [homeassistant.bootstrap] Invalid config for [script]: [delay] is an invalid option for [script]. Check: script->script->say->sequence->2->delay.

Anyone any Ideas?

Thanks for the quick answer.
Indeed the first answer is incorrect due to single quotes.
So, it is working fine with double quotes:
- delay: "00:00:{{ ('0' ~ states.media_player.living_room.attributes.media_duration) | reverse | truncate(2, True, '') | reverse }}"

First I got the same error (invalid config…) but I had a mistake in the script:
-delay:
- delay: {00:00:00}
So, recheck the file for typos or bad identation.

@Gabriel_Bratescu It works, because you are using “media_player.living_room” directly instead of the “where” variable within the delay template.

So, the issue with this

- delay: "00:00:{{ ('0' ~ states.[where].attributes.media_duration) | reverse | truncate(2, True, '') | reverse }}"

is passing over the [WHERE] variable into the template for the delay. Anybody knows how and if that works?

As long this is not solved I would recommend using just something like

       # Add a delay in a length which you think fits best for all the things you want to say over TTS
       - delay: '00:00:07'