TTS to media_player and resume stream

Thanks for your post. Using your post I was able to create a script myself.
Since I’m using a routine in my Google Assistant settings, I only use a script, and no automation.
I’m also not using the variables plugin from HACS, but the built in helpers in HA (input_text and input_number)

For this script to work, you’ll need to add the input_number.is_playing and input_text.chromecast_stream.

Just placing it here because it might help someone.

trash_container_script:
  alias: Which trash container
  sequence:
  - service: input_number.set_value
    entity_id: input_number.is_playing
    data_template:
      value: >
        {% if is_state('media_player.kitchen_hub', 'playing')  %}
        1
        {% else %}
        0
        {% endif %}
  - service: input_text.set_value
    entity_id: input_text.chromecast_stream
    data_template:
      value: > 
        {% if is_state('input_number.is_playing', '1.0') %}
        {{ state_attr('media_player.kitchen_hub', 'media_content_id') }}
        {% else %}
        http://icecast.omroep.nl/radio2-bb-aac
        {% endif %}
  - service: tts.google_translate_say
    entity_id: media_player.keuken_hub
    data_template:
      message: "The {{states('sensor.next_container')}}. should be place outside {{ state_attr('sensor.next_container, 'when') }} . "
      cache: false
  - delay: '00:00:08'
  - condition: numeric_state
    entity_id: input_number.is_playing
    above: 0.1
  - service: media_extractor.play_media
    data_template:
      entity_id: media_player.kitchen_hub
      media_content_id: >
        {{ states('input_text.chromecast_stream') }}
      media_content_type: "music"

2020-09-23 - I’ve edited this post because I could simplify the script a bit, and added the part on how to call this script from another script or automation

2020-11-06 - Another small edit, because I noticed that service: media_extractor was not needed, and media_player.play_media could be used
I’ve also added a wait template instead of the fixed delay of 8 seconds which was previously included. Left in a 2 second delay because the media player is idle shortly before the TTS starts, and otherwise the TTS would be skipped

For those who stumble upon this posts. As from version 0.115 you can use variables in Home Assistant without additional plugins. I’ve rewritten my script to this. It resumes both TuneIn radio and Spotify.
For Spotify you’ll need Spotcast installed though. And in my script I assumed the Spoify account used on the Google Home is always the same account (the one Spotcast also uses)

kitchen_hub_say:
  alias: Kitchen - TTS for Google Nest Hub in Kitchen
  sequence:
  - variables:
      spotify: >-
        {{ is_state_attr('media_player.keuken_hub', 'app_name', 'Spotify') }}
      playing: >-
        {{ is_state('media_player.keuken_hub', 'playing')  }}
      media_content: >-
        {{ state_attr('media_player.kitchen_hub', 'media_content_id') }}
      media_type: >-
        {{ state_attr('media_player.kitchen_hub', 'media_content_type') }}
  - service: tts.google_translate_say
    entity_id: media_player.kitchen_hub
    data:
      message: "{{ kitchen_hub_say }}"
      cache: false
  - delay: 2
  - wait_template: "{{ is_state('media_player.kitchen_hub', 'idle') }}"
  - service: media_player.turn_on
    entity_id: media_player.kitchen_hub
  - choose:
    - conditions: "{{ spotify }}"
      sequence:
      - service: spotcast.start
        data:
          device_name: Kitchen Hub
    - conditions: "{{ playing }}"
      sequence:
      - service: media_player.play_media
        data:
          entity_id: media_player.kitchen_hub
          media_content_id: "{{ media_content }}"
          media_content_type: "{{ media_type }}"

The media_player.turn_on lines just after the TTS part are to make sure the TTS screen isn’t being displayed on the hub anymore after it is finished.

The script can be called like this from another script or automation:

- service: script.turn_on
  entity_id: script.kitchen_hub_say
  data:
    variables:
      kitchen_hub_say: "Enter text or template containing the text to be sent to the script"
5 Likes

Hi @TheFes I have a question.

I have several google minis around my house and I was wondering if there’s a way to adapt this script in order to be used with several device.

I tried with templating but I’m not good with it!

Thank you!

The same way I stopped using this script, is to use the “broadcast” command for google home minis.
What this does is pause whats playing, say the message and continue automatically.
But it plays on all google home minis. Don’t know if you want this.

Yes I want this but I noticed that if the message contains some word messages are not delivered. So I’m searching for an alternative

Some questions:

  • Do you always want to use the same Google Mini devices for the TTS?
  • Are they playing the same media, or could it be that one is playing radio chanel 1, the other chanel 2, and a third Spotify?

If they don’t play the same media, you would need to create device specific variables, so e.g. spotify_mini_kitchen, and separate actions to play it again. However if you do play the same media, it could become out of sync. It will be a bit tricky I guess.
For playing the same media on seperate devices, I guess you could use speaker groups in the Google Home app, but then you would need to build in additional checks if a spearker group is used.

Sorry I lost the notification.

  1. no I want to use different mini based on different messages and time (avoid bedroom at night for example)

  2. sometimes they play same device from spotify. Sometimes me and my wife listen different musics.

Hi

Sorry from my side as well, notification got lost in my email.
But I’m afraid that does make it a bit more complex, and I currently don’t have the time to dig into it. Working on a big list of HA projects for my own house as well (and my boss also wants me to do my work I get paid for ;))

1 Like

yes you’re right! :slight_smile:

Verry nice,

I try to change the script and detect the casting speaker group and spotify account usage. I have some troubles with my script, but i can’t find out where.

Maybe someone sees it:

My action:

  action:
    - service: script.run_tts_say
      data_template: 
        tts_say: "Test"
        tts_to: "reserve"
        tts_volume: "0.6"

And the script:

run_tts_say:
  alias: TTS Spotify test resume
  sequence:
  - variables:
      spotify: >-
        {{ is_state_attr('media_player.{{ tts_to }}', 'app_name', 'Spotify') }}
      playing: >-
        {{ is_state('media_player.{{ tts_to }}', 'playing')  }}
      media_content: >-
        {{ state_attr('media_player.{{ tts_to }}', 'media_content_id') }}
      media_type: >-
        {{ state_attr('media_player.{{ tts_to }}', 'media_content_type') }}
  - service: tts.google_say
    data:
      entity_id: "media_player.{{ tts_to }}"
      message: "{{ tts_say }}"
      cache: false
  - delay: 2
  - wait_template: "{{ is_state('media_player.{{ tts_to }}', 'idle') }}"
  - service: media_player.turn_on
    entity_id: media_player.{{ tts_to }}
  - choose:
    - conditions: "{{ spotify }}"
      sequence:
      - variables:
          spot_castingto: >-
            {% if is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Reserve') %}
              {{ 'reserve' }}
            {% elif is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Woonkamer') %}
              {{ 'woonkamer' }}
            {% elif is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Keuken') %}
              {{ 'keuken' }}
            {% elif is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Thuisgroep') %}
              {{ 'thuisgroep' }}   
            {% elif is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Beneden') %}
              {{ 'beneden' }}  
            {% elif is_state("media_player.spotify_asterixonline", "playing") and is_state_attr('media_player.spotify_asterixonline','source','Test') %}
              {{ 'test' }}                                              
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Reserve') %}
              {{ 'reserve' }}
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Woonkamer') %}
              {{ 'woonkamer' }}
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Keuken') %}
              {{ 'keuken' }}
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Thuisgroep') %}
              {{ 'thuisgroep' }}  
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Beneden') %}
              {{ 'beneden' }}   
            {% elif is_state("media_player.spotify_joelle", "playing") and is_state_attr('media_player.spotify_joelle','source','Test') %}
              {{ 'test' }}       
            {% else %}
              {{ tts_to }}
            {% endif %}  
          spcast_account: >-
            {% if is_state("media_player.spotify_joelle", "playing") %}
              'joelle'  
            {% else %}
            {% endif %}              
      - service: spotcast.start
        data:
          device_name: "{{ spot_castingto }}"
          account: "{{ spcast_account }}"
      - delay: 4          
      - service: media_player.media_play
        target:
          entity_id: media_player.{{ spot_castingto }}
    - conditions: "{{ playing }}"
      sequence:
      - service: media_player.play_media
        data:
          entity_id: media_player.{{ tts_to }}
          media_content_id: "{{ media_content }}"
          media_content_type: "{{ media_type }}"   

Thanx for advice.

“some troubles” isn’t a lot of information to start debugging :stuck_out_tongue:
Any issues/warnings in your log? Does it work partly, or not at all? etc etc etc.

1 Like

I think the if, else is not working somehow, I get no issues when saving from file editor. But when reloading scripts it gives me a warning about variables. The script is not valid as it don’t show up as a script. I’ll post the error .

The if else then works .

This is an issue:

  - variables:
      spotify: >-
        {{ is_state_attr('media_player.{{ tts_to }}', 'app_name', 'Spotify') }}

the media player identity is not working. and therefore the rest of the script isnt.
Later in the script i use it for sending google tts, and that works well.

  - service: tts.google_say
    data:
      entity_id: "media_player.{{ tts_to }}"
      message: "{{ tts_say }}"
      cache: false

Verry strange.

I didn’t find out how to solve this.

So, i’m verry close, it works with spotify, but when a second message is there, the source status give me a wrong source. When it’s playing on two or more speakers, it give me only one speaker. Maybe that’s an issue with spotcast.

For now the action looks like:

  action:
    - service: script.run_tts_say
      data_template: 
        tts_say: "Test Bericht"
        tts_to: reserve #speaker you want to cast to
        tts_volume: 0.4

And the script:

run_tts_say:
  alias: TTS Spotify test resume
  sequence:
  - variables:
      tts_curvolume: >-
        {{ state_attr('media_player.'~tts_to, 'volume_level') }}
      spcast_joelle: >-
        {{ is_state('media_player.spotify_joelle', 'playing') }}
      spotify: >-
        {{ is_state_attr('media_player.'~tts_to, 'app_name', 'Spotify') }}
      playing: >-
        {{ is_state('media_player.'~tts_to, 'playing') }}
      media_content: >-
        {{ state_attr('media_player.'~tts_to, 'media_content_id') }}
      media_type: >-
        {{ state_attr('media_player.'~tts_to, 'media_content_type') }}
  - service: media_player.volume_set
    data_template:
      entity_id: media_player.{{ tts_to }}
      volume_level: "{{ tts_volume }}"
  - service: tts.google_say
    data:
      entity_id: media_player.{{ tts_to }}
      message: "{{ tts_say }}"
      cache: false
  - delay: '00:00:03'
  - wait_template: "{{ is_state('media_player.'~tts_to, 'idle') }}"
  - choose:
    - conditions: "{{ spotify }}"
      sequence:
      - choose:
        - conditions: "{{ spcast_joelle }}"
          sequence:
          - variables:
              spot_castingto: >-
                {{ state_attr('media_player.spotify_joelle', 'source') }}
          - delay: '00:00:01'            
          - service: spotcast.start
            data:
              entity_id: media_player.{{ spot_castingto }}
              force_playback: true          
              account: joelle
        - conditions: []
          sequence:
          - variables:
              spot_castingto: >-
                {{ state_attr('media_player.spotify_asterixonline', 'source') }}
          - delay: '00:00:01'            
          - service: spotcast.start
            data:
              entity_id: media_player.{{ spot_castingto }}
              force_playback: true          
      - delay: '00:00:01'         
      - service: media_player.volume_set
        data_template:
          entity_id: media_player.{{ tts_to }}
          volume_level: "{{ tts_curvolume }}" 
    - conditions: "{{ playing }}"
      sequence:
      - service: media_player.play_media
        data:
          entity_id: media_player.{{ tts_to }}
          media_content_id: "{{ media_content }}"
          media_content_type: "{{ media_type }}"
      - service: media_player.volume_set
        data_template:
          entity_id: media_player.{{ tts_to }}
          volume_level: "{{ tts_curvolume }}"
    - conditions: []
      sequence:  
      - service: media_player.volume_set
        data_template:
          entity_id: media_player.{{ tts_to }}
          volume_level: "{{ tts_curvolume }}"    

I use multiple spotify accounts, so there is a detection build in.

Maybe someone can use it.

@Reijer and others :slight_smile:
I’ve been doing some updates to the script to support resuming playback on speaker groups and multiply Spotify accounts. I’ve also made it generic, so it can be used for all the Google Home speakers, and not only one specific speaker.

Prerequisites and comments for this new version:

  • You need to create groups in home assistant with the media_players belonging to the speaker groups in the Google Home app. This is used for the player_resume variable
  • My speaker group media_player.home_group contains all my Google Home speakers, so that is why I did not include a check if the speaker is belonging to that group for the player_resume variable
  • In spotcast there is one primary account, you need to enter that one in the variable primary_spotcast
  • For Spotify you need to add the accounts to the Spotify integration, and also to spotcast. Be sure to name the spotify entity_id’s like media_player.spotify_{{ spotcast user }}
  • After the script has been called (e.g. because it announced the doorbell rang) the app_name for radio will no longer be TuneIn Free. So the script did not recognize anymore that radio was playing. As a work around I added a variable where the first part of the TuneIn url of frequently used radio stations can be added, so the media_content_id can be compared to that list.
  • I’ve added a variable where you can add Google Nest Hubs (the ones with screen). If a TTS was played on such a device, and no music is resumed, the TTS cast icon was not removed from the screen.
  • I’m using Microsoft TTS, you eed to change the TTS service to yours if you are not using Microsoft
  • I’ve added fields, so you see which variables can be entered when using the GUI, both in yaml mode, and full GUI. player and tts_message are required. If volume is not entered it will use the volume_old variable for the TTS volume (so basically it will remain the same)
  • There is a short delay for updating the media_content_id attribute for the Spotify integration. I use that to determine which spotcast account should be used. So therefor in case there is only one Spotify account active, it will use that one. In case there is more than one active and the new song just started, it could be that the account is not recognized. In that case the primary account will be used.

The script can be started like this in an automation or script:
In case you want to have your script/automation wait for the TTS script to be completed

  - alias: "TTS voor Nest Hub Kitchen"
    service: script.google_home_say
    data:
      tts_message: "Hello, hello! This is a test!"
      player: media_player.google_kitchen
      volume: 0.35

In case you want your script/automation to resume with the next action immediately

  - alias: "TTS voor Nest Hub Kitchen"
    service: script.turn_on 
    target:
      entity_id: script.google_home_say
    data:
      variables:
        tts_message: "Hello, hello! This is a test!"
        player: media_player.google_kitchen
        volume: 0.35

And this is then the amended script:

google_home_say:
  alias: "TTS for Google Home"
  icon: mdi:cast-audio
  mode: parallel
  fields: 
    tts_message:
      description: "Message to be uses as TTS for Google Home."
      example: "Hello, this is a test message."
      required: true
      selector:
        text:
    player:
      description: "The target Google Home."
      example: media_player.google_keuken
      required: true
      selector:
        entity:
          integration: cast
          domain: media_player
    volume:
      description: "Volume for TTS message (value between 0 and 1)."
      example: 0.25
      required: false
      selector:
        number:
          min: 0
          max: 1
          step: 0.05
          mode: slider
  variables: # General variables for script
    players_screen:
      - media_player.google_kitchen
    # TuneIn stream url without http(s):// until first /. Used because app_name 
    # is not visible after this script has been used before in the same session
    frequent_radio: 
      - icecast.omroep.nl
      - playerservices.streamtheworld.com
    # Make sure the entity_id's match this format, and the last part should 
    # match the spotcast account
    spotify_media_players: 
      - media_player.spotify_user1
      - media_player.spotify_user2
      - media_player.spotify_user3
    primary_spotcast: "user1"
  sequence:
    - alias: "Variables for this specific run of the script"
      variables: 
        volume_old: >
          {{ state_attr(player, 'volume_level') | default('0.25', true) | round(2) }}
        spotify: >
          {{ is_state_attr(player, 'app_name', 'Spotify') }}
        radio: >
          {{ 
            is_state_attr(player, 'app_name', 'TuneIn Free') 
            or (state_attr(player, 'media_content_id')
              .split('//')[1]| default('no/tunein', true)).split('/')[0]
              in frequent_radio 
          }}
        media_content: >
          {{ state_attr(player, 'media_content_id') | default('geen', true) }}
        player_resume: >
          {% if is_state('media_player.home_group', 'playing') %}
            media_player.home_group
          {% 
            elif ((player in state_attr('group.first_floor_group', 'entity_id')) 
            and is_state('media_player.first_floor_group', 'playing')) 
          %}
            media_player.first_floor_group
          {% 
            elif ((player in state_attr('ground_floor_group', 'entity_id')) 
            and is_state('media_player.ground_floor_group', 'playing')) 
          %}
            media_player.ground_floor_group
          {% else %}
            {{ player }}
          {% endif %}
        screen: "{{ player in players_screen }}"
        spotcast_account: >
          {% 
            set spotify_playing = expand(spotify_media_players) 
                                  | selectattr('state', 'eq', 'playing')
                                  | map(attribute='entity_id') 
                                  | list 
                                  | count 
          %}
          {% if spotify_playing == 1 %}
            {{ 
              ( 
                expand(spotify_media_players) 
                | selectattr('state', 'eq', 'playing')
                | map(attribute='entity_id') 
                | join
              ).split('_')[2] 
            }}
          {% else %}
            {{ 
              (
                expand(spotify_media_players) 
                | selectattr('attributes.media_content_id', 'eq', media_content)
                | map(attribute='entity_id') 
                | join
              ).split('_')[2]  | default(primary_spotcast, true)
            }}
          {% endif %}
    - alias: "Apply TTS volume"
      service: media_player.volume_set
      target:
        entity_id: "{{ player }}"
      data:
        volume_level: "{{ volume | default(volume_old, true) }}"
    - alias: "Send TTS message"
      service: tts.microsoft_say
      data:
        entity_id: "{{ player }}"
        message: "{{ tts_message }}"
    - alias: "Short delay to make sure the TTS message has started"
      delay: 2
    - alias: "Wait until TTS message is complete"
      wait_template: "{{ states(player) in ['idle', 'off'] }}"
    - alias: "Set volume back to old state"
      service: media_player.volume_set
      target:
        entity_id: "{{ player }}"
      data:
        volume_level: "{{ volume_old }}"
    - alias: "Google Home with screen?"
      choose: 
        - conditions: "{{ screen }}"
          sequence:
            - alias: "Turn Google Home on to return to idle mode (photo display)"
              service: media_player.turn_on
              target:
                entity_id: "{{ player }}"
    - alias: "Was something playing?"
      choose:
        - alias: "Spotify?"
          conditions: "{{ spotify }}"
          sequence:
            - alias: "Primary spotcast account?"
              choose:
              - conditions: "{{ spotcast_account == primary_spotcast }}"
                sequence:
                  - alias: "Resume Spotify with primary spotcast account"
                    service: spotcast.start
                    data:
                      entity_id: "{{ player_resume }}"
              default:
                - alias: "Resume Spotify with specific account"
                  service: spotcast.start
                  data:
                    entity_id: "{{ player_resume }}"
                    account: "{{ spotcast_account }}"
        - alias: "Radio?"
          conditions: "{{ radio }}"
          sequence:
            - alias: "Resume radio"
              service: media_player.play_media
              target:
                entity_id: "{{ player_resume }}"
              data:
                media_content_id: "{{ media_content }}"
                media_content_type: "music"
2 Likes

I posted a new version here:

2 Likes

Nice!
Sorry I was away from this, but I started Node-Red and also using broadcasts.
I’m gonna mark it as a solution so people can directly go to your link.

I kinda highjacked your post anyway. What started as a few improvements on your solution became a bit bigger step by step :slightly_smiling_face:
Thanks!

Νο probs mate, happy that someone brought it to the next level :slightly_smiling_face:

1 Like

Not to revive an old thread but another way to go about it is having snapcast have multiple streams where mpd is going to have lowest priority, bluetooth second and vlc (setup as a telnet reciever, where you’d send your tts or whatever noise you want to)

Like so
Snapserver.conf

source = pipe:///dev/shm/snapserver/snapfifo?name=Storm
source = pipe:///dev/shm/snapserver/bluefifo?name=Bluetooth
source = pipe:///dev/shm/snapserver/vlcfifo?name=VLC
stream = tcp://192.168.0.11?port=4953&name=Broadcast
source = meta:///VLC/Broadcast/Bluetooth/Storm?name=Mix

Start vlc like so on the machine running mpd/mopidy and snapserver(preferably as a service/daemon):

vlc -I telnet --telnet-pasword=yourpassword vlc --no-video --aout afile --audiofile-file /dev/shm/snapserver/vlcfifo

Sorry for the missing code tags, on my phone and can’t find the tags