SONOS TTS Script

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'

I managed to specify a dynamic delay that should be about the duration of the spoken text by the following script. Apparently the initial 1s delay is necessary for the duration in the state to be filled.

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 %}
      {{ [hours, minutes, seconds]|join(':') }}
  - service: media_player.sonos_restore
    data_template:
      entity_id: "{{ 'media_player.' ~ where }}"

Hope that helps. :slight_smile:

11 Likes

@tleilax Awesome! Thanks! :slight_smile:

GREAT WORK!! Is there a way to set configurations for “sonos_restore”? I find the sliding volume to the original state annoyingly slow.

/Tonkin

By the way: Im having tons of fun with this:

curl -X POST -H "x-ha-access: {{mysecretcode}}" \
       -H "Content-Type: application/json" \
       -d '{"where": "livingroom", "what": "Hello my friend"}' \
       http://192.168.1.3:8123/api/services/script/say

:smiley:
Tonkin

{% set duration = states.media_player[where].attributes.media_duration %}

does this mean you are setting your where variable to “.living_room” with the leading period? How did you manage to pass the variable into the set statement?

Okay, I was able to figure out the nuances here. I think I’ve got it. I’m using a similar format to create a recursive script to transition lights that don’t support it natively.

I might be stupid but I can’t get it to work. When i call the test_ttl script nothing happens for me.


configuration.yaml
script: !include_dir_merge_named includes/scripts


scripts/sonos_tts.yaml

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 %}
        "{{ [hours, minutes, seconds]|join(':') }}"
    - service: media_player.sonos_restore
      data_template:
        entity_id: "{{ 'media_player.' ~ where }}"

scripts/test_tts.yaml

test_tts:
  alias: Test for TTS
  sequence:
    - service: script.turn_on
      entity_id: script.say
      data:
        variables:
          where: 'media_player.kitchen'
          what: 'Test.'

@christian.ek The new version only expects where: ‘kitchen’, since media_player. is already part of the script and templates.

Found the error… I renamed it sonos_tts but when calling the script i used script.say

1 Like

Thanks for the great script! I added some features to really make sure that the message is played only in the specific media player (unjoin first) and not to the whole group that it is connected to. On top of that I changed the volume so it wouldnt use the same volume as the speaker was set to.

    # > SONOS TTS
  say:
    alias: SONOS TTS
    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.45
      - service: tts.google_say
        data_template:
          entity_id: "{{ 'media_player.' ~ 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:
          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 %}
          {{ [hours, minutes, seconds]|join(':') }}
      - service: media_player.sonos_restore
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"
          with_group: yes
1 Like

Cant get this to work :frowning: It complaining about invalid config. It dosnt like the automation part i think?

17-02-19 23:49:24 ERROR (MainThread) [homeassistant.bootstrap] Invalid config for [automation]: [service] is an invalid option for [automation]. Check: automation->service. (See /home/hass/.homeassistant/configuration.yaml, line 163). Please check the docs at https://home-assistant.io/components/automation/
Failed config

Heres my config

coniguration.yaml
# Google Text to speech  
tts:
  - platform: google
    cache: true
    cache_dir: /tmp/tts
    time_memory: 300
    language: 'sv'

# Include
group: !include groups.yaml
sensor: !include sensors.yaml
switch: !include switches.yaml
script: !include_dir_merge_named script
automation: !include_dir_merge_list automation

script/sonos_tts.yaml
# > SONOS TTS
  say:
    alias: SONOS TTS
    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.45
      - service: tts.google_say
        data_template:
          entity_id: "{{ 'media_player.' ~ 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:
          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 %}
          {{ [hours, minutes, seconds]|join(':') }}
      - service: media_player.sonos_restore
        data_template:
          entity_id: "{{ 'media_player.' ~ where }}"
          with_group: yes

automation/sonos_test.yaml
- service: script.turn_on
  entity_id: script.say
  data:
    variables:
      where: 'kontor'
      what: 'Hej där!'

Could you post the hole content of your automation since the error you posted is about the automation, not about the script.

I couldn’t get it to work with the {{ where }} and {{ what }} either.
Now that I have placed the “where” and the “what” within the actual scripts, it works fine again…

Maby Im doing this wrong but the hole automation is:

- service: script.turn_on
  entity_id: script.say
  data:
    variables:
      where: 'kontor'
      what: 'Hej där!'

it is just for test before I include it in some automation that I have running. I have tried include it in this one but without luck.

- alias: "Mail vattenläcka diskbänk"
  initial_state: True
  hide_entity: False
  trigger:
    - platform: state
      entity_id: switch.fuktdiskbank
      to: 'on'
  action:
    - service: notify.gmailtoni
      data_template:
        title: "Vattenläcka under diskbänk"
        message: "Möjlig vattenläcka under diskbänk kontrollera"
    - service: notify.gmailmalin
      data_template:
        title: "Vattenläcka under diskbänk"
        message: "Möjlig vattenläcka under diskbänk kontrollera"
    - service: script.turn_on
      entity_id: script.say
      data:
        variables:
          where: 'kontor'
          what: 'Hej där!'