Hoping for help with announcement notification using multiple sonos speakers (announcement cuts off mid sentence 1-3 seconds in)

Where in the script does it reference a ‘sonos_group’?

All I can find in the script is that it picks one speaker, unjoins it from all others, and then plays the TTS message to that one speaker.

Also, if it selects a speaker, why is the script hard-coded to wait for ‘media_player.living_room_sonos_speaker’?

In the cloud say service call. In the else clause.

- service: tts.google_cloud_say
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.sonos_group
            {% endif %}
          message: >-
            {{ message }}

Because I don’t want the living room media player changing the volume back to its default until the media player goes back to “paused”, i.e. finished announcment.

I also have no clue what I’m doing though, so if this stuff is just wrong, I wouldn’t turn down guidance. I just don’t understand why the upstairs player would stop while the bottom one keeps going if they’re in the same group.

Something is causing the living room one to stop for some reason.

So if the else clause selects to play to:
media_player.back_porch_speaker

why would it wait for a completely different player: media_player.living_room_sonos_speaker

to change state to paused?

Shouldn’t it wait for the speaker it chose to play to?

I’ll add to Taras answer: all my sonos devices are in idle at the end of the tts, not paused.

Clearly there is some logic somewhere but mine definitely (almost always) are paused.
:man_shrugging:

It’s my understanding that a call to tts.speak or tts.<whatever> gets converted to a call to media_player.play_media and its announce option is automatically set to true. Users have reported that when announce is true, the player doesn’t report playing while it’s playing the announcement.

In other words, while playing the announcement, it doesn’t change state to playing and then, when it’s done, to either idle or paused but just remains either idle/paused. That makes it challenging to determine when the announcement has finished playing.

If you take that into account, plus the fact that this script isn’t necessarily monitoring the state of the media_player that’s handling the announcement, it’s clear that it will experience problems.

For starters, you have to fix the part that is hard-coded to wait for the living room speaker and, unless you really must have the announce feature, you may wish to use media_player.play_media directly with announce set to false.

The following example is untested and may require adjustments to work in your environment.

service: media_player.play_media
data:
  media_content_id: 'media-source://tts/google_cloud?message=Your message goes here'
  media_content_type: 'provider'
  announce: false
target:
  entity_id: media_player.your_chosen_player

This is true. And despite the obvious goodness of the new announce capability I cannot beleive it got through the QA process. I still maintain that the state (database/machine/engine - whatever it is called) should not be allowed to be wrong

Which is also true except you are being too kind with your choice of word. Challenging it isn’t. Impossible it is.

But hey ho, You can’t have everything.

PS I believe this is an issue related to the Sonos api being used but as an outsider I find it hard to understand why some extra ‘announce’ attributes couldn’t be added to the media entity to facilitate working out when the announcement has started and finished.

You’re making the bold assumption that the hardware in question reports it’s state during announcements. The state machine only holds information that it gets from the device it communicates with. If the device does not report that information, is the state machine wrong? No.

E.g. Alexa doesn’t support this, so announcing on Alexa will never show a “announcement” status. I.e. Alexa media player cannot change it’s state to ‘playing’ when it’s announcing because that state is not provided by the hardware.

I see what you are saying. In my case the else clause to play to is the Sonos group.

I agree with you on your second statement though.

I should change my wait for pause to also use the Sonos group media player and see if this issue goes away.

I’ll report back and post new code either way.

Thank you @123

I didn’t realize any of this. Thank you for your understanding. I will look/ try this if my last reply doesn’t seem to correct anything.

Again ‘from the outside’, it still seems to me that the integration itself must ‘know’ it is making an announcement so it just ‘feels’ to me that the integration could add/set an attribute to that effect, along with another one that (at the least) says how long the announcement is.

I have no ‘real world’ knowledge about how integrations work under the hood or if that is even possible so I am just speaking as ‘user’.
I’m more than happy to be told it is impossible.
And although it would be nice I don’t even need someone to give me an explanation.
I’m certainly not looking to stir anything up :grin:

(Although, I will never be comfortable that it is ok for the state of a device to be knowingly wrong)

It’s not going to know that if the hardware doesn’t tell you how fast it’s going to announce. Like I said, you’re making assumptions. What happens if your hardware has a voice that speaks fast? You have another that speaks slow. Only the hardware knows this information because it’s handling the announcement. Therefor, only it can tell us how long the response is going to take.

But tts announcements are simply an mp3 created at run time and then played.

Maybe that isn’t the case for announce, I don’t know, I haven’t had time to experiment with it yet partly because the issue we’re talking about breaks a lot of my announcement system so will need some rewriting.

I do intend to get round to it one day because as I said, the new feature is good.

I’m fairly sure it does not create an MP3 file before streaming it. It’s just a service that causes it to say the words it receives. EDIT: for the tts.cloud_say, it may be that way.

ok, so here is what I have changed the script to. Does anyone see any issues with this one. I have not tested it yet but have simplified it (honestly more than I would like but it is what it is). I’m at my wants vs. abilities threshold for this one I believe.

Change the snapshot and restore to the “media_player.sonos group” entity, this is a media player group including 2 sonos speakers

Commented out wait template and just hardcoded 45 second delay before changing volume back to regular volume. Can you change the volume on a media player group that contains 2 sonos speakers?

I really don’t care for the 45 second hard code but I don’t know of any other way if the original looking for the pause state won’t work properly due to hardware limitations?

Thank you all for your input!!

  speech_engine:
    mode: queued
    sequence:
      # - service: mqtt.publish
      #   data_template:
      #     topic: 'house/polly/lastmsg'
      #     payload: 'This message is from {{ now().strftime("%-I") }}:{{ now().strftime("%M") }} {{ now().strftime("%p") }}. {{ message | striptags | truncate(220)}}'
      #     retain: true
      # - service: mqtt.publish
      #   data_template:
      #     topic: 'house/polly/lastloc'
      #     payload: '{{ who }}'
      #     retain: true
      - condition: state
        entity_id: input_boolean.audible_notifications
        state: "on"
      - condition: state
        entity_id: group.family
        state: "home"
      - condition: state
        entity_id: input_boolean.vacation_mode
        state: "off"
      - service: sonos.snapshot
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.sonos_group
            {% endif %}
      # - service: media_player.unjoin
      #   data_template:
      #     entity_id: >
      #       {% if who == 'back porch' %}
      #         media_player.back_porch_speaker
      #       {% elif who == 'front porch' %}
      #         media_player.front_porch_speaker
      #       {% elif who == 'dining room' %}
      #         media_player.dining_room_wifi
      #       {% elif who == 'office' %}
      #         media_player.office_wifi
      #       {% elif who == 'master bedroom' %}
      #         media_player.master_bedroom_speaker
      #       {% elif who == 'master bathroom' %}
      #         media_player.master_bathroom_speaker
      #       {% elif who == 'gym' %}
      #         media_player.gym_speaker
      #       {% elif who == 'guest bedroom' %}
      #         media_player.guest_bedroom_speaker
      #       {% elif who == 'garage' %}
      #         media_player.garage_speaker
      #       {% elif who == 'guest bathroom' %}
      #         media_player.guest_bathroom_speaker
      #       {% elif who == 'everywhere inside' %}
      #         media_player.everywhere_inside
      #       {% elif who == 'kitchen' %}
      #         media_player.kitchen_display
      #       {% else %}
      #         media_player.living_room_sonos_speaker
      #       {% endif %}
      - service: media_player.volume_set
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.living_room_sonos_speaker
            {% endif %}
          volume_level: >
            {{ states('input_number.tts_volume') | float }}
      - service: tts.google_cloud_say
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.sonos_group
            {% endif %}
          message: >-
            {{ message }}
      - delay: 00:00:45
      # - wait_template: >-
      #     {{ states('media_player.living_room_sonos_speaker') == 'paused' }}
      - service: media_player.volume_set
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.living_room_sonos_speaker
            {% endif %}
          volume_level: >
            {{ states('input_number.current_speaker_volume') | float }}
      - service: sonos.restore
        data_template:
          entity_id: >
            {% if who == 'back porch' %}
              media_player.back_porch_speaker
            {% elif who == 'front porch' %}
              media_player.front_porch_speaker
            {% elif who == 'dining room' %}
              media_player.dining_room_wifi
            {% elif who == 'office' %}
              media_player.office_wifi
            {% elif who == 'master bedroom' %}
              media_player.master_bedroom_speaker
            {% elif who == 'master bathroom' %}
              media_player.master_bathroom_speaker
            {% elif who == 'gym' %}
              media_player.gym_speaker
            {% elif who == 'guest bedroom' %}
              media_player.guest_bedroom_speaker
            {% elif who == 'garage' %}
              media_player.garage_speaker
            {% elif who == 'guest bathroom' %}
              media_player.guest_bathroom_speaker
            {% elif who == 'everywhere inside' %}
              media_player.everywhere_inside
            {% elif who == 'kitchen' %}
              media_player.kitchen_display
            {% else %}
              media_player.sonos_group
            {% endif %}

Maybe this is a stupid question but how is announce much different from snapshot then restore?

I guess one is more generalized since its for the media player service rather than just for the sonos service.

Also, can you help me figure out how to use this idea properly.

Below is what I think I need to do but I don’t really know how to handle media_content_id because my media is contained in a parameter or variable I believe it’s called. i.e. {{message}} that I am providing to the tts.google_cloud_say service

media content type, do I put music? It looks like I only have 6 choices based on the documentation
media content id, no clue what to provide based on my paragraph above

service: media_player.play_media
data:
  media_content_id: 'media-source://tts/google_cloud?message={{message}}'
  media_content_type: music
  announce: false
target:
  entity_id: media_player.sonos_group

you should use variables

  speech_engine:
    mode: queued
    variables:
      players:
        'back porch': media_player.back_porch_speaker
        'front porch': media_player.front_porch_speaker
        'dining room': media_player.dining_room_wifi
        office: media_player.office_wifi
        'master bedroom': media_player.master_bedroom_speaker
        'master bathroom': media_player.master_bathroom_speaker
        gym: media_player.gym_speaker
        'guest bedroom': media_player.guest_bedroom_speaker
        garage: media_player.garage_speaker
        'guest bathroom': media_player.guest_bathroom_speaker
        everywhere inside: media_player.everywhere_inside
        kitchen: media_player.kitchen_display
      player: >
        {{ players.get(who, 'media_player.sonos_group') }}
    sequence:
      # - service: mqtt.publish
      #   data_template:
      #     topic: 'house/polly/lastmsg'
      #     payload: 'This message is from {{ now().strftime("%-I") }}:{{ now().strftime("%M") }} {{ now().strftime("%p") }}. {{ message | striptags | truncate(220)}}'
      #     retain: true
      # - service: mqtt.publish
      #   data_template:
      #     topic: 'house/polly/lastloc'
      #     payload: '{{ who }}'
      #     retain: true
      - condition: state
        entity_id: input_boolean.audible_notifications
        state: "on"
      - condition: state
        entity_id: group.family
        state: "home"
      - condition: state
        entity_id: input_boolean.vacation_mode
        state: "off"
      - service: sonos.snapshot
        data_template:
          entity_id: "{{ player }}"
      # - service: media_player.unjoin
      #   data_template:
      #     entity_id: "{{ player }}"
      - service: media_player.volume_set
        data:
          entity_id: "{{ player }}"
          volume_level: >
            {{ states('input_number.tts_volume') | float }}
      - service: tts.google_cloud_say
        data:
          entity_id: "{{ player }}"
          message: >-
            {{ message }}
      - delay: 00:00:45
      # - wait_template: >-
      #     {{ states('media_player.living_room_sonos_speaker') == 'paused' }}
      - service: media_player.volume_set
        data:
          entity_id: "{{ player }}"
          volume_level: >
            {{ states('input_number.current_speaker_volume') | float }}
      - service: sonos.restore
        data:
          entity_id: "{{ player }}"

Thank you for showing me that!! Very helpful!!

You can try this instead:

      - delay:
          seconds: "{{ (message.split() | count / 2) | round(0) }}"

It counts the number of words in the message, divides by two and rounds the result. In other words, it gives each word a half-second to be spoken.

If the spoken message is occasionally cut off, just tinker with the calculation to get a slightly longer delay. For example:

      - delay:
          seconds: "{{ (message.split() | count * 0.6) | round(0) }}"