Script for Sonos Speakers to do Text-to-Speech and Handle Typical Oddities

Glad the blueprint is working for you!

Hey @krazykaz83 , I misunderstood your comment back in July (been away for a bit), but I reread and understand; youā€™re referring to delay after the wait for it to play. Yup, looking at media duration is a good idea. I appreciate the input! Iā€™ll use that as a reference for an update. Two questions:

  1. Was there a reason you had this in your code snippet?
  {% if duration > 0 %} 
     {% set duration = duration %} 
  {% endif %}
  1. You mention the need for a five second delay to load the media_duration, was there really that long of a delay? Thatā€™s a lot of time to have to wait, particularly before restoring the state of the sonos after the message should have played.

Side-note: I was hoping not to have this delay at all, but the Sonos states arenā€™t accurate. The state checks like "{{ states( entity_id ) != 'playing' }}" frequently fire too early. That is the same issue that @mkammes mentioned regarding the volume setting. Iā€™m going to fix that today by adding a min wait to use. May help address multiple wait issues.

@mkammes, apologies of the large delay, but I just added a new field min_wait you can optionally set. It will force a minimum wait after two key state changes that seem to report early in HA. One is after setting the volume when music is playing. The other is waiting for the system to stop playing before it restores Sonos back to the original state. I tested a variety of examples and all seemed to work for me, but let me know how it works for you. Youā€™ll have to play with how much delay is needed, but it defaults to 0.

@krazykaz83 , I added a variation of your suggestion to the code and in conjunction with the above changes, modified some existing delay logic. If you are still seeing cut-offs, my recommendation is to set the max_wait to be large (since it doesnā€™t force it, and should rely on Sonos state change logic to at least get close to correct) and then use the min_wait for a forced delay (Iā€™m making a bit of assumption that the reporting of early state changes is consistent). I am still curious about your comment regarding the media_duration taking long to set/load. In my tests today I didnā€™t experience that behaviour on a few speakers but depending on what you report I may look at additional changes.

Thanks!

So great of you to share this! I like announcing stuff too so had a similar setup but I prefer scripts and had written a script with a similar function. It can play multiple files or tts in multiple speakers, with an automated delay process (thanks to this thread). Iā€™m sharing ā€œas isā€ in case you want to incorporate some of it to your blueprint.

alias: Announce with TTS
sequence:
  - service: sonos.snapshot
    data:
      entity_id: "{{ v_speaker_names | join(', ') }}"
      with_group: false
  - service: media_player.repeat_set
    data:
      repeat: "off"
      entity_id: "{{ v_speaker_names | join(', ') }}"
  - service: media_player.volume_set
    data:
      volume_level: "{{ v_volume_level | float }} "
    target:
      entity_id: "{{ v_speaker_names_for_volume | join(', ') }}"
  - repeat:
      for_each: "{{ v_media_details }}"
      sequence:
        - if:
            - condition: template
              value_template: "{{ ' ' in repeat.item.v_media_to_play }}"
          then:
            - service: tts.google_cloud_say
              data:
                entity_id: "{{ v_speaker_names | join(', ') }}"
                message: "{{ repeat.item.v_media_to_play }}"
                cache: true
          else:
            - service: media_player.play_media
              target:
                entity_id: "{{ v_speaker_names | join(', ') }}"
              data:
                media_content_id: "{{ repeat.item.v_media_to_play }}"
                media_content_type: audio/mpeg
              metadata: {}
        - delay: 2
        - delay: >-
            {{ [((state_attr(v_speaker_names | first, 'media_duration') |
            int) - 1), 0] | max }}
  - service: sonos.restore
    data:
      entity_id: "{{ v_speaker_names | join(', ') }}"
      with_group: false
mode: queued
icon: mdi:speaker-message
max: 10

it is called from an automation in the following way:

alias: Announce with TTS test
description: ""
trigger: []
condition: []
action:
  - service: script.announce_with_tts
    data:
      v_speaker_names:
        - media_player.speaker1
        - media_player.speaker2
      v_speaker_names_for_volume:
        - media_player.speaker1
      v_volume_level: 0.4
      v_media_details:
        - v_media_to_play: media-source://media_source/local/ding_dong.mp3
        - v_media_to_play: >-
            <speak>Someone is at the <break time='200ms'/><emphasis
            level='strong'>front door</emphasis></speak>
mode: single
1 Like

Hi, Iā€™am not used to use blueprints, but it works to send a massage to 1 Sonos speaker. The mode is standing in parallel, but how can I add more speakers for the same message? So that I can hear the message on all my speakers. Do I have to add more names in de entity field? How do I have to do that? Thanks for the help.

Hey @pepe2, messages can be sent to more than one speaker by putting comma-separated names on the entity_id field BUT you cannot guarantee that the playback will be syncā€™ed since they arenā€™t in a Sonos group. Here is a screenshot using the dev interface showing this (Iā€™m using YAML mode instead of UI mode):

Two notes:

  1. Iā€™ve found that when you have more than one speaker like this, HA seems to return too quickly from the playback and can cut-off the message. You can use the min_wait to force a wait, which you see me doing in the screenshot.
  2. If you keep your Sonos speakers grouped (e.g. using the Sonos app), then this script will play across the grouped speakers even if you only put one speaker in entity_id; Sonos takes care of it automatically.

If you donā€™t keep your speakers grouped together and want the blueprint to take care of grouping (to ensure playback is syncā€™ed) let me know and I can look to add later. If I do this, I will need to play around some to ensure the pre-existing groups and any music playing properly continue after the message playback. It is doable, but more complicated.

I just posted a minor update to this script that ensures volume levels are correct before using the value and I added a wait (controlled by min_wait) to help ensure very short messages (e.g ā€˜Helloā€™) play back properly.

Much appreciated suggestion @saint ! I would likely build a script that sat on top of mine, that looks very much like what you have, taking the list of media and conditionally doing media playback or calling my current script. To be internationally aware (or support for single word messages) I would look to see about using something other than a space as the condition. Curious if checking if the string conformed to a URL regex would be sufficient??

Are you using this to announce music before playing it?

Thanks, it is working. The sonos speakers are in different rooms, so it is no problem if they are not synced.

Totally new to Sonos, does this work with Ikea Symfonisk speakers?

Want to grab a few of these to serve as always on speakers to convey TTS alerts but do not want to use anything like an Alexa, Echo, etc.

Sure, my pleasure.

Regex would probably work as well (or even just checking http / media-source within the string). I thought itā€™s unlikely Iā€™ll use a single word announcement, but good point overall - itā€™s even less likely Iā€™ll use the string ā€œhttpā€ or ā€œmedia-sourceā€ within an announcement :slight_smile:

I actually donā€™t announce music. My use case is more like announce in multiple sonos speakers, and/or a combination of a sound and a speech (like a chime sound and some announcement, or an alarm sound and an announcement).

Yes, it work on Symfonisk as well. :slight_smile:

Thanks for confirming @bjorn.sivertsen!

There are also the Ikea speaker lamps ā€¦ add a Hue / colour changing bulb and you an audio/visual notification experience :slight_smile: .

OH I like that, putting a chime followed by the notification. Hmm, maybe Iā€™ll look to create an extended version to do that (just finishing another script at the moment first).

But man, I really wish it was easier to have dependencies between blueprints. I wrote a proposal for it but didnā€™t get much traction (see here). If I had more spare time Iā€™d pick up Python, dive into the HA codebase and see about adding it myself.

2 Likes

totally share the feeling, so many exciting things to do and only 24 hours a day :slight_smile: Good luck with your other script.

Yup, definitely know what you mean!

Well, finally got the other script up (itā€™s here if curious) and need to see about whatā€™s next (including your chime suggestion).

Hey @matthewcbyington , I missed this message, and I saw you posted elsewhere. You still need some help?

Hi, great script, thank you for all your effort. Iā€™m amended it slightly to play a single media file locally, which works great. As Sonos is a mult-speaker system, could I please ask how you play this to a group of speakers? stacking multiple actions doesnā€™t work. Can you group speakers in HA as a entity?

Sorry Iā€™ve just seen your other blue print, Play Media.
Thatā€™s basically what I wanted to do, but to snapshot the current state of Sonos and restore it after.