SONOS TTS Script

Here’s my updated Sonos TTS script that is working very well for me. You can find more details, including others scripts using the TTS script in my repo here.

  sonos_say:
    alias: "Sonos TTS script"
    sequence:
      - service: media_player.sonos_snapshot
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroomsonos') }}"
      - service: media_player.sonos_unjoin
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroomsonos') }}"
      - service: media_player.volume_set
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroomsonos') }}"
          volume_level: "{{ volume|default(0.5) }}"
      - service: tts.google_say
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroomsonos') }}"
          message: "{{ message }}"
      - delay: "{{ delay|default('00:00:00') }}"
      - wait_template: "{{ is_state(sonos_entity|default('media_player.livingroomsonos'), 'playing') }}"
        timeout: '00:00:05'
      - wait_template: "{{ not is_state(sonos_entity|default('media_player.livingroomsonos'), 'playing') }}"
        timeout: '00:02:00'
      - service: media_player.sonos_restore
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroomsonos') }}"
6 Likes

@arsaboo Thanks for sharing… Will give it a go…

In my experiments I was having more success with local media playing and by adding a delay of 1 second right before the sonos_restore.

@arsaboo Your script appears to work MUCH better for me!

One odd thing is that for local audio it seems absolutely perfect with speech starting almost instantly and then resuming quickly… But for streaming audio there is a delay before it starts speaking of 3 - 4 seconds…

Now I understand a delay when it resumes and it needs to restart streaming… But why a delay at the beginning?

I’m struggling to get this to work.

I’m using the script from @arsaboo

Getting the following in the logs:

2017-07-31 23:18:16 WARNING (MainThread) [homeassistant.helpers.entity] Update for media_player.living_room is already in progress
2017-07-31 23:18:16 WARNING (MainThread) [homeassistant.helpers.entity] Update for media_player.living_room is already in progress
2017-07-31 23:18:16 WARNING (MainThread) [homeassistant.helpers.entity] Update for media_player.living_room is already in progress

I’m playing music from either a spotify playlist or from my iPhone. The TTS plays almost instantly using google_say, then stops… and stays. Doesn’t seem to restore.

Script (called, script.sonos_tts):

sequence:
  - service: media_player.sonos_snapshot
    data_template:
      entity_id: "{{ sonos_entity|default('media_player.living_room') }}"
  - service: media_player.sonos_unjoin
    data_template:
      entity_id: "{{ sonos_entity|default('media_player.living_room') }}"
  - service: media_player.volume_set
    data_template:
      entity_id: "{{ sonos_entity|default('media_player.living_room') }}"
      volume_level: "{{ volume|default(0.3) }}"
  - service: tts.google_say
    data_template:
      entity_id: "{{ sonos_entity|default('media_player.living_room') }}"
      message: "{{ message }}"
  - delay: "{{ delay|default('00:00:00') }}"
  - wait_template: "{{ is_state(sonos_entity|default('media_player.living_room'), 'playing') }}"
    timeout: '00:00:05'
  - wait_template: "{{ not is_state(sonos_entity|default('media_player.living_room'), 'playing') }}"
    timeout: '00:02:00'
  - service: media_player.sonos_restore
    data_template:
      entity_id: "{{ sonos_entity|default('media_player.living_room') }}"

Script 2:

  - service: script.sonos_tts
    data:
      sonos_entity: media_player.living_room
      volume: 0.2
      delay: '00:00:04'
      message: 'Testing!'

It restores after 2 minutes. Remove the second wait_template and it should restore immediately after the TTS playback. Also, remove the delay from the script call.

Are you sure? The 1st wait template waits for the TTS to start to play; with a 5 second timeout. Then the second waits for it to finish playing (not playing state); with a 2 minute timeout.

If I remove the 2nd wait longer TTS messages get cutoff as it restores as soon as it starts to play.

You need the wait_template only if you are not using delay. The problem with the second wait_template is that it sometimes does not restore until the timeout if Sonos was already playing something before the TTS.

Running a very simple script trying to get it to work and still having issues. It is creating the audio file in the TTS folder, but nothing happens and it just sits there. I can play it manually so it does not appear to be corrupt. Any clues as to what could be the issue here?

Thanks

test_sonos:
sequence:
- service: media_player.sonos_snapshot
data_template:
entity_id: media_player.study
- service: tts.google_say
data_template:
entity_id: media_player.study
message: “Test message.”
- delay: “00:00:{{ states.media_player.study.attributes.media_duration | int }}”
- service: media_player.sonos_restore
data_template:
entity_id: media_player.study

It works for me when there is something already playing.
When the Sonos is inactive there is no speech coming out.

I am having a similar issue as @badgerhome, I am also using the script form @arsaboo as this is the one that I like best, although I have tried other scripts on this page as well. Basicly, I have 1 out of 3 Sonos speakers that doesn’t restart playing after the announcement. Here is my setup:

Sonos Play1 --> office
Sonos Play3 --> kitchen
Sonos Play5 --> living room

Playing music and if I send a TTS announcement to my “office” speaker, I get the voice, but it never returns back to music although the other speakers continue playing. Sending the same announcement to either the kitchen or the livingroom works well with no issues, music restarts after the announcement, In summary, here are my results:

office --> music playback Does Not work
kitchen --> music playback works
livingroom --> music playback works

Of course, I want most of my announcements in the office, so I’m a little disappointed. I almost think that it’s a big in the way that the snapshot and restore works on the first speaker that was installed (office) Would this be considered my Master Speaker?

Any chance that this somehow could be fixed by a script?
IF not part of a group and music was playing, restore music
IF part of a group and music was playing, re-join the group

Any other ideas?

If you post your script I can try to help. I have no issues with my music being restored after the tts to either one or multiple speakers

@Jer78 - Thanks, much appreciated

Here is my Sonos TTS Script

  sonos_tts:
    alias: "Sonos TTS script - Testing"
    sequence:
      - service: media_player.sonos_snapshot
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroom') }}"
      - service: media_player.sonos_unjoin
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroom') }}"
      - service: media_player.volume_set
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroom') }}"
          volume_level: "{{ volume|default(0.5) }}"
      - service: tts.google_say
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroom') }}"
          message: "{{ message }}"
      - delay: "{{ delay|default('00:00:00') }}"
      - wait_template: "{{ is_state(sonos_entity|default('media_player.livingroom'), 'playing') }}"
        timeout: '00:00:05'
      - wait_template: "{{ not is_state(sonos_entity|default('media_player.livingroom'), 'playing') }}"
        timeout: '00:02:00'
      - service: media_player.sonos_restore
        data_template:
          entity_id: "{{ sonos_entity|default('media_player.livingroom') }}"

Here is my Announcement:

  notificationgaragedoor:
    alias: 'Garage Door Open'
    sequence:
      - service: script.sonos_tts
        data:
          sonos_entity: 'media_player.office'
          volume: 0.35
          delay: '00:00:04'
          message: 'Garage door is open, please check!'

Here is my automation that calls the announcement, I just change the time everytime I want to test.

- alias: "Testing Sonos TTS"
  initial_state: True
  hide_entity: False
  trigger:
    platform: time
    at: '14:17:00'
  action:
    service: script.turn_on
    entity_id: script.notificationgaragedoor

@berniebl Do you happen to have another Sonos speaker acting as the “master”? If so, if you unjoin them after the snapshot but aren’t rejoining them maybe that’s causing the issue?? I’d try commenting out the unjoin service and see what happens.

I’m using one of the earlier methods and seems to work rock solid for me. Although this looks interesting, I can see where it has its limitations.

Well, this surely gave me better results, I don’t know why I didn’t think of that, but I think that I need to take a look at what a “master” is and if there is a way to not configure one of my speakers this way. Still not sure if one of them is or not.

What is happening now is that the announcement is broadcasted to All speakers, but the music starts … at least the music starts back now, but it forces a new track. I can live with the new track, but the wife will not appreciate having a message broadcasted over every speakers. I can always use this script if I need a broadcast to All Speakers, but will need to research what this “master” speaker is all about and how to reconfigure …

Thanks for leading me in this direction, I appreciate your help!

Well, at least it’s a step in the right direction! Glad we somewhat isolated the issue. Later tonight or weekend, I’ll post my script. It is hard coded, but in my case, I always wanted it to go to one particular speaker and it does resume properly if they were all previously grouped. This is probably what you want in this one case.

Here’s my script. I first play music in the morning and then depending on my motion sensor, it tells me my morning update or speaks a little louder to get up! lol Anyways, it works really well as it uses the bedroom sonos speaker and resumes all other speakers music afterwards:

  morning_announcement:
    sequence:
      - service: media_player.sonos_snapshot
        data_template:
          entity_id: "media_player.sonos_master_bedroom"
      - service: media_player.media_pause
        entity_id: "media_player.sonos_master_bedroom"
      - service: media_player.volume_set
        data_template:
          entity_id: >
            {% if is_state("group.guest_mode_options", "off") %}
              {% set players = "media_player.sonos_master_bedroom, media_player.sonos_living_room, media_player.sonos_office" %}
            {% else %}
              {% set players = "media_player.sonos_master_bedroom" %}
            {% endif %}
            {{ players }}
          volume_level: >-
           {%- set vol = (states.input_slider.speech_volume.state | int) -%}
           {%- set adjvol = vol / 50 -%}
           {%- set newvolume = adjvol | float -%}
           {{ newvolume }}
      - service: tts.amazon_polly_say
        data_template:
          entity_id: "media_player.sonos_master_bedroom"
          message: >-
            <speak>
            {% if is_state("sensor.bedroom_occupancy", "Yes") %}
              Good morning, I see that you are up. In case you missed it, here is your morning briefing again.
              {% if is_state("input_boolean.morning_speech_weather", "on") %}The temperature outside is currently {{ states.sensor.dark_sky_apparent_temperature.state  | round(0)  }} degrees. {% endif %}
              {% if is_state("input_boolean.morning_speech_forecast", "on") %}The weather for the day is {{ states.sensor.dark_sky_hourly_summary.state }} {% endif %}
              {% if is_state("input_boolean.morning_speech_calendar", "on") %}Your first appointment is {{ states.sensor.first_calendar_title.state }} at {{ states.sensor.first_calendar_start_time.state }}. {% endif %}
              {% if is_state("input_boolean.morning_speech_travel", "on") %}It will take you {{ states.sensor.first_google_travel_time.state }} minutes travel time to get there. {% endif %}
              {% if is_state("input_boolean.morning_speech_reminder", "on") %} {{ states.sensor.todoist_all.state }}.  {% endif %}
              {%- set secondr = ["Have a great day.","Have a good one.","Goodbye","See you later alligator","Talk to you tonight"] -%} {% set sindex = (range(0, (secondr | length - 1) )|random) -%} {{secondr[sindex]}}
            {% else %}
              Sorry to interrupt, but you asked me to wake you up, and I do not see any movement in the bedroom. <emphasis>Please Wake up!</emphasis>
            {% endif %}
            </speak>
      - delay:
          seconds: 5
      - delay: >-
          {% set duration = states.media_player.sonos_master_bedroom.attributes.media_duration %}
          {% if duration > 0 %}
            {% set duration = duration - 5 %}
          {% endif %}
          {% set seconds = duration % 60 %}
          {% set minutes = (duration / 60)|int % 60 %}
          {% set hours = (duration / 3600)|int %}
          {{ [hours, minutes, seconds]|join(':') }}
      - service: script.turn_on
        entity_id: script.increase_volume
      - service: media_player.sonos_restore
        data_template:
          entity_id: "media_player.sonos_master_bedroom"
3 Likes

Thanks, I’ll surely give it a shot when there is no one here. Don’t want to aggravate the family too much or the project will be nixed. Also seems like you have some other good morning announcements that I may take a stab at. Thanks

Just to chip in, i have 2 sonos speakers which acts as a stereo pair. If i remove the “unjoin” function it restores correctly. If i don’t remove that line it will not restore the music after tts. Might be the cause for some too.

Nice man. I like it. I do a lot with my sonos similar one thing I can never get a time based automation to work. Would you mind sharing the automation?

@Darbos Here’s my automation and sensors. I have an alarm for every day of the week which is configurable through input_sliders. You could just hard code it but I prefer the ability to change as needed.

First up, create the iinput_sliders to configure the hour and minutes.

  alarm_hour_sat:
    name: Hour
    icon: mdi:timer
    initial: 9
    min: 0
    max: 23
    step: 1
  alarm_minutes_sat:
    name: Minutes
    icon: mdi:timer
    initial: 00
    min: 0
    max: 55
    step: 5

Next, we need to create some sensors:

- platform: template
  sensors: 
    alarmtime_sat:
       friendly_name: Wakeup Time
       entity_id:
         - input_slider.alarm_hour_sat
         - input_slider.alarm_minutes_sat
       value_template: '{{ "%0.02d:%0.02d" | format(states("input_slider.alarm_hour_sat") | int, states("input_slider.alarm_minutes_sat") | int) }}'

Next is the automation. Note, I have a bunch of input_boolean conditions you can take out. I have set them up because I have multiple ways to configure an alarm. One for each day of the week, another automation runs if the option to use the same alarm time every day, and finally there’s a pretty cool “ideal alarm time” based off my first calendar appointment. Also, when I’m on vacation mode, I don’t want it to run. But you’d have to set those input_booleans separately. I left them in to give you ideas.

  - alias: 'Saturday Alarm'
    initial_state: on
    trigger:
      platform: time
      minutes: '/5'
      seconds: '0'
    condition:
      condition: and
      conditions:
        - condition: time
          weekday:
            - sat
        - condition: template
          value_template: '{{ ((now().strftime("%s") | int ) | timestamp_custom("%H:%M")) == states.sensor.alarmtime_sat.state  }}'
        - condition: state
          entity_id: input_boolean.alarm_clock_sat
          state: 'on'
        - condition: state
          entity_id: input_boolean.alarm_clock_all
          state: 'off'
        - condition: state
          entity_id: input_boolean.vacation_mode
          state: 'off'
        - condition: state
          entity_id: input_boolean.use_ideal_alarm_clock
          state: 'off'
    action:
      - service: scene.turn_on
        data:
          entity_id: scene.goodmorning
1 Like