SONOS TTS Script

The other thing to check is see if you have a file in the TTS folder. If not, that may be part of the problem too.

This causes issues for me as well if tts is not able to fully load the file from Google to HA and then to Sonos. Is it possible to ensure that tts.google_say ends up loading the file and starting playing it before the script continues?

If I run the code without the . (where the config check says it’s all good), then the TTS plays on the Sonos speaker, but everything after that does not execute. So the sonos_restore command is not executed and the speaker just goes silent.

The error as stated above still remains the same, except the duration is being put in, just not formatted correctly with leading zeros if they are needed.

So I guess the problem has nothing to do with the . missing, as you stated above. It’s more to do with the formatting of the values [seconds, minutes, hours] and not having a leading zero where the value is less than 10.

If I comment out the below section of the code it works normally. I hear “Hallo” from my Sonos and after a few seconds (I changed delay to 4 seconds) the TV sounds resumes again.

- delay: >-
    {% set duration = states.media_player[where].attributes.media_duration %}
    {% if duration > 0 %}
      {% set duration = duration - 1 %}
    {% endif %}
    {% set seconds = duration % 60 %}
    {% set minutes = (duration / 60)|int % 60 %}
    {% set hours = (duration / 3600)|int %}
    "{{ [hours, minutes, seconds]|join(':') }}"

What about this one please:

https://community.home-assistant.io/t/script-issue-with-tts-and-sonos-mediaplayer-my-house-thank-you-p/17026

Hi guys, I’ve been fidling with this script to use my sonos system as you do. I’ve found a solution for the duration issue. The problem is: the expected format for the duration is HH:MM:SS but this script was sending H:M:S.

The main change is in the duration string formatting (HH:MM:SS):

{{ “%02i:%02i:%02i”|format(hours, minutes, seconds)}}

The full script below:

sonos_tts:
  alias: Sonos Text To Speech
  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: "{{ what }}"
    - delay:
        seconds: 1
    - delay: >-
        {% set duration = states.media_player[where].attributes.media_duration %}
        {% if duration > 0 %}
          {% set duration = duration - 1 %}
        {% endif %}
        {% set seconds = duration % 60 %}
        {% set minutes = (duration / 60)|int % 60 %}
        {% set hours = (duration / 3600)|int %}
        {{ "%02i:%02i:%02i"|format(hours, minutes, seconds)}}
    - service: media_player.sonos_restore
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"

Now the script works pretty well, and its even faster to resume the sonos state!

Good luck!

3 Likes

UPDATE:

My script fixes the time format issue but it takes too long to restore the media player. Im seeing loads of entries in the logs like these:

2017-06-03 11:57:40 WARNING (MainThread) [homeassistant.helpers.entity] Update for media_player.bureau is already in progress

It seems to me that media_player.sonos_restore is taking too long, maybe because the home assistant node is in a different network and creaing upnp issues… ill try moving the HASS to the same neetwork.

Finally. MOved the HASS node to the local lan and this is working much better.

Just got this working with a combination of the above scripts. For anyone that wants it, this script works perfectly for me, no error logs and it removes the speaker from the group before doing the TTS. Thanks for all the comments and hard work!

sonos_tts:
  alias: Sonos Text To Speech
  sequence:
    - service: media_player.sonos_snapshot
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        with_group: yes
    - service: media_player.sonos_unjoin
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
    - service: media_player.volume_set
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        volume_level: 0.70
    - service: tts.google_say
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        message: "{{ what }}"
# Workout the length of the media
    - delay:
        seconds: 1
    - delay: >-
        {% set duration = states.media_player[where].attributes.media_duration %}
        {% if duration > 0 %}
          {% set duration = duration - 1 %}
        {% endif %}
        {% set seconds = duration % 60 %}
        {% set minutes = (duration / 60)|int % 60 %}
        {% set hours = (duration / 3600)|int %}
        {{ "%02i:%02i:%02i"|format(hours, minutes, seconds)}}
    - service: media_player.sonos_restore
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        with_group: yes
3 Likes

Has anyone made this work with spotify connect?
My sonos does not continue playing with spotify connect after the tts.

2 Likes

Wow, so complex! Today the whole duration thing can be fixed by using wait_template in a manner similar to this:

script:
  tts_wait_test:
    sequence:
      - service: tts.google_say
        data:
          entity_id: media_player.your_media_player
          message: "Hello World. I am your personal assistant."

      - wait_template: >-
          {{ is_state('media_player.your_media_player', 'playing') }}
        timeout: '00:00:03'

      - wait_template: >-
          {{ not is_state('media_player.your_media_player', 'playing') }}
        timeout: '00:01:00'

      - service: tts.google_say
        data:
          entity_id: media_player.your_media_player
          message: "I waited until I finished saying the previous statement, so that I can say this one."
2 Likes

This seems to work…

########################################################
## Sonos TTS
########################################################
say:
  alias: SONOS TTS
  sequence:
    - condition: state
      entity_id: group.household
      state: 'home'

    - service: media_player.sonos_snapshot
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        with_group: yes

    - service: media_player.sonos_unjoin
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"

    - service: media_player.volume_set
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        volume_level: 0.4

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

    - wait_template: >-
        {{ is_state('media_player.' ~ where , 'playing') }}
      timeout: '00:00:15'

    - wait_template: >-
        {{ not is_state('media_player.' ~ where , 'playing') }}
      timeout: '00:01:00'

#    - delay:
#        seconds: 2

#    - delay: >-
#        {% set duration = states.media_player[where].attributes.media_duration %}
#        {% if duration > 0 %}
#          {% set duration = duration - 1 %}
#        {% endif %}
#        {% set seconds = duration % 60 %}
#        {% set minutes = (duration / 60)|int % 60 %}
#        {% set hours = (duration / 3600)|int %}
#        {{ "%02i:%02i:%02i"|format(hours, minutes, seconds)}}

    - service: media_player.sonos_restore
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"
        with_group: yes
2 Likes

This works great as far as returning to whatever was playing on the Sonos, but it seems to cut short at random points. Say I want it to say “This is a test”. The results could be:

This is a test (4 times in a row with no problems)
This is a te
This is a tes
This is a
This

With the full sentence being peppered in a few times along the way. I can’t see any cause for it.

Are your Sonos Ethernet or wifi?

They’re both. One is connected via ethernet and it is meshing with the others.
It doesn’t appear to a problem with networking though as in the web ui I can see that when I run the sonos_tts_test script the say_sonos script it calls usually gets turned off prematurely.

Here’s the code I am using:

  sonos_tts_test:
    sequence:
      - service: script.turn_on
        entity_id: script.say_sonos
        data:
          variables:
            where: 'sonos_office'
            what: 'This is a test and it is too short'

  say_sonos:
    sequence:
      - service: media_player.sonos_snapshot
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"
          with_group: yes

      - service: media_player.sonos_unjoin
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"

      - service: media_player.volume_set
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"
          volume_level: 0.4

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

      - wait_template: >-
          {{ is_state('media_player.' ~ where , 'playing') }}
        timeout: '00:00:03'

      - wait_template: >-
          {{ not is_state('media_player.' ~ where , 'playing') }}
        timeout: '00:01:00'

      - service: media_player.sonos_restore
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"
          with_group: yes
1 Like

Try changing the timeout on the 1st wait template to 30 seconds. I think what might be happening is by the time the songs starts playing the 3 seconds has passed and it’s timed out.

The first wait is to wait for sonos to start to play; the second to wait for songs to stop playing…

Good idea. 30 seconds would be FAR too long but I will experiment with increasing the delay. I might set up another sensor monitoring the state so I can see how quickly that updates.

The way I would be using this is to interrupt music (or even TV the Sonos Playbar) to give a notification. i.e. The garage door is open or the like.

Hi, I’m having trouble getting the script to work correctly, I have used code above:

when I try it I get the following error:
This is the error:
2017-07-17 20:48:56 ERROR (MainThread) [homeassistant.core] Invalid service data for media_player.sonos_snapshot: Entity ID media_player. is an invalid entity id for dictionary value @ data[‘entity_id’]. Got ‘media_player.’

As far as I can tell, none of the variables are being passed to the script - Entity ID media_player. when it should be Entity ID media_player.sonos_office I think?

And apologies in advance, I’m very new to this so may be missing something very obvious, really appreciate your help.

(Hassbian - .49)

Thanks

Yes, I suggest 30 to just test; and then fine tune to something that makes sense.

I just tried copy/pasted the last example from @mihalski but my sonos is not restoring it’s state. I tried playing a song then manually activate the script to say something, it says the text but then it never returns to the music.

However i see this in the log, don’t know it it’s related :

2017-07-17 14:01:01 WARNING (MainThread) [homeassistant.helpers.entity] Update for media_player.portable is already in progress