Google Assistant Broadcast Feature

Problem to solve:
tts.google_say interupts any media playing on a device and overrides it with the mp3 file with the speach

Solution:
Use the “broadcast” feature of the Google Assistant instead.

Further Explanation:
If I use Google Assistant on my smartphone and type to it “Broadcast [insert broadcast]” it will send those messages to my Google Homes throughout the house as Google Assistant speaking them, and if I say certain things like “Dinner Time” or “Good Morning” or “Goodnight” it broadcasts specific little Google Assistant clips for the message instead. I’d like for Home Assistant to be able to sent a notification to my Google Homes this way. Sure, the tts.googlesay technically can do something functionally similar to this already, but it overrides any media that may be playing on the speaker, where the broadcast feature does not.

I understand this would likely require an API key of some kind, just like the “sync devices” button does in the Google Assistant component, so maybe this just is an extra feature for that component specifically. I’d like to be able to say notify.googlehome “Dinner’s ready” and for HA to do the same thing that me doing “Broadcast Dinner’s ready” would do.

I haven’t managed to get this working yet but others have:

There is already one.

in my experience though, google assistant broadcast is garbage. not in sync and on multiple gh around the house, all being out of sync is yuck

Yeah, I found that too and have been having problems figuring out how to get it set up, which is why I’m requesting it be built into the Google Assistant component specifically. It feels like more of a hacky solution than what it could be if it were just built in to the Google Assistant component as a notify platform instead of being an addon

I guess it depends on your setup, if you have them all at max volume and close enough that you have the sounds overlapping I can see why that would be annoying, but I definitely would still prefer having them not be in sync to them having music/whatever cut off and totally stopped by a tts announcement from HA.

i have 9 of them around the house, doing music is fine, do a broadcast, oh god its not pretty

i also found an another project that does the same thing, but requires a separate server. but this has some built in commands ready to go.
which maybe an alternative

1 Like

I’ve got one in each room too, and it’s definitely not pleasant if one or more has the volume too high and they overlap with other speakers in the house, but even so, out of sync broadcasts are still MUCH more preferential to having existing playback overridden by a tts

I’ll look into that too, thanks!

i even tried the broadcast feature for broadcast the alarm being armed and disarmed. soon went back to tts to google group

Well, I use it all the time manually and love it, and if I use it from home assistant I’m sure it would be even better.

I’m still surprised Google implemented broadcasts this way. Especially when there usual use case is human voice broadcasts around the home. Hopefully in the future they will add some synchronisation.

Made this feature request, which is a bit more broad than this request, but also would include this request.

Linking for reference: Add a service call for the ability to send a text command to Google Assistant

1 Like

I’ve posted a workaround for this , it might help.

Saludos;
En este caso especifica en el que google home interrumpia todas los media player identificados como “cast” la solucion fue la siguiente en automation.yaml.

“entity_id: media_player.google_home
message: johanna ha salido de casa
service: tts.google_translate_say”

es decir que luego de llamar el servicio solo se active la entidad_id especifica.

Captura

Now with google u can broadcast to a single device, would be cool to implement that feature, as now the static robotic sound is not good.

If broadcast would be enabled it uses the default sound without the standard google tts sound

you dont have to use your voice like, hey google: broadcast to kitchen… and it will ask what message.
But on my phone when i type the message to the kitchen it will announce the typed text to the kitchen speaker with the default voice instead of the tts standard robotic version

+1 for a way to utilize “Broadcast” instead of using TTS Say that just cuts off all currently playing media and doesn’t resume from where you were.

2 Likes

I use a combination of unclesbob’s and tts.google_cloud_say. Essentially I prefer tts.google_cloud_say but it will interrupt. So I wrote a script that will check if all the entities are busy or not. If they aren’t it will use tts.google_cloud_say. If they are, it will use a combination of this broadcast for the busy speakers and the normal tts.google_cloud_say for the non-busy ones. It also (mostly) defeats the wabamm sound it makes prior to broadcasting when speakers are idle at least. It depends on having groups named the same as speaker groups so I can expand them and check them separately and control the volume/mute separately since google had to remove the speaker group volume management). IT also relies on a set of input_numbers as ‘default’ volume’ settings per speaker to return its volume to.

alias: TTS No Interrupt
sequence:
  - if:
      - condition: template
        value_template: |-
          {% set entities = expand(target) %}
          {% set ready = entities
            | selectattr('state','in',('idle','off','unavailable','paused'))
            | list | length %}
          {% set hamedia = entities
            | rejectattr('state','in',('idle','off','unavailable','paused'))
            | selectattr("attributes.app_name", 'defined')
            | selectattr('attributes.app_name','in',('Home Assistant Media Player','Home Assistant Lovelace Player','Default Media Receiver'))
            | map(attribute='entity_id') | list | length %}
          {{ (ready + hamedia) ==  (entities | list | length) }}
    then:
      - service: media_player.volume_set
        data:
          volume_level: 0.001
        data_template:
          entity_id: '{{ expand(target)| map(attribute=''entity_id'') | join('', '') }}'
      - delay:
          hours: 0
          minutes: 0
          seconds: 0
          milliseconds: 300
      - choose:
          - conditions:
              - condition: template
                value_template: '{{media_content_id | length > 0 }}'
            sequence:
              - service: media_player.play_media
                target:
                  entity_id: '{{target.replace(''group.'',''media_player.'')}}'
                data:
                  media_content_id: '{{ media_content_id }}'
                  media_content_type: '{{ media_content_type }}'
                metadata: {}
          - conditions:
              - condition: template
                value_template: '{{ message | count > 0 }}'
            sequence:
              - service: script.turn_on
                data:
                  variables:
                    message: '{{message|default(''One Test Message One'',true) }}'
                    entity_id: '{{target.replace(''group.'',''media_player.'')}}'
                target:
                  entity_id: script.cloud_say
        default: []
      - if:
          - condition: template
            value_template: '{{volume | default(0)  > 0 }}'
            alias: Custom volume requested.
        then:
          - service: media_player.volume_set
            data:
              volume_level: '{{ volume | default(''0.5'') }}'
            data_template:
              entity_id: |-
                {% if is_state('input_boolean.company','on') %}
                   {{ expand(target) | map(attribute='entity_id') | reject('==','media_player.back_office_speaker') | join(', ')  }}
                {% else %}
                   {{ expand(target)| map(attribute='entity_id') | join(', ') }}
                {% endif %}
          - delay: '{{ delay_to_stop | default(10) }}'
      - service: script.media_player_volume_set
        alias: Correct the volume after the custom volume delay.
        data:
          entity_id: |-
            {% if is_state('input_boolean.company','on') %}
              {{ (expand(target) | map(attribute='entity_id') | reject('==','media_player.back_office_speaker') | join(', ')).split(', ')  }}
            {% else %}
              {{ (expand(target)| map(attribute='entity_id') | join(', ')).split(', ') }}
            {% endif %}
      - delay:
          hours: 0
          minutes: 0
          seconds: 0
          milliseconds: 200
      - if:
          - condition: template
            value_template: |-
              {{ expand('media_player.kitchen_display')
                | rejectattr('state','in',('idle','off','unavailable','paused'))
                | selectattr('attributes.app_name', 'defined')
                | selectattr('attributes.app_name','in',('Home Assistant Media Player','Home Assistant Lovelace Player','Default Media Receiver'))
                | list | count > 0 
                and  'media_player.kitchen_display' in expand(target) | map(attribute='entity_id') | join(', ') }}
          - condition: template
            value_template: '{{ stop_display | default(true) }}'
        then:
          - wait_for_trigger:
              - platform: state
                entity_id: media_player.kitchen_display
                from: playing
                to: idle
            timeout: |-
              {% if delay_to_stop | default(0) > 0 %}
                1
              {% else %}
                {{ delay_to_stop | default(10) }}
              {% endif %}
          - service: media_player.turn_off
            target:
              entity_id: media_player.kitchen_display
            data: {}
    else:
      - if:
          - condition: template
            value_template: '{{ expand(target) | count > 0 }}'
        then:
          - variables:
              available: |-
                {% set entities = expand(target) %}
                {% set ready = entities 
                  | rejectattr('state','in',('idle','off','unavailable','paused'))
                  | selectattr("attributes.app_name", 'defined') 
                  | selectattr('attributes.app_name','in',('Home Assistant Media Player','Home Assistant Lovelace Player','Default Media Receiver'))
                  | map(attribute='entity_id') 
                  | list %}
                {% set haready = entities  
                  | selectattr('state','in',('idle','off','unavailable','paused'))
                  | map(attribute='entity_id') 
                  | list %}
                {% if is_state('input_boolean.company','on') %}
                  {{ (haready + ready) | unique | reject('==','media_player.back_office_speaker') | list }}
                {% else %}
                  {{ (haready + ready) | unique | list }}
                {% endif %}
              busy: |-
                {% set entities = expand(target) %}
                {% set busy = entities 
                  | rejectattr('state','in',('idle','off','unavailable','paused'))
                  | selectattr("attributes.app_name", 'defined') 
                  | rejectattr('attributes.app_name','in',('Home Assistant Media Player','Home Assistant Lovelace Player','Default Media Receiver'))
                  | map(attribute='entity_id') 
                  | list %}
                {% if is_state('input_boolean.company','on') %}
                  {{ busy | reject('==','media_player.back_office_speaker') | list }}
                {% else %}
                  {{ busy }}
                {% endif %}
          - if:
              - condition: template
                value_template: '{{available|count > 0}}'
            then:
              - service: tts.google_cloud_say
                data:
                  entity_id: '{{available}}'
                  message: '{{message|default("Test Message",true) }}'
          - repeat:
              count: '{{ busy | count }}'
              sequence:
                - variables:
                    speaker: '{{ busy[repeat.index - 1] }}'
                - if:
                    - condition: template
                      value_template: '{{ message | length > 0 }}'
                  then:
                    - service: script.turn_on
                      data:
                        variables:
                          message: >-
                            broadcast to {{ state_attr('' ~
                            speaker,'friendly_name') }}, {{message|default('Test
                            Message',true) }}
                      target:
                        entity_id: script.run_ga_command
        else:
          - if:
              - condition: template
                value_template: '{{ message | length > 0 }}'
            then:
              - service: script.turn_on
                data:
                  variables:
                    message: >-
                      broadcast to {{ state_attr('' ~ speaker,'friendly_name')
                      }}, {{message|default('Test Message',true) }}
                target:
                  entity_id: script.run_ga_command
mode: parallel
fields:
  message:
    description: Message to send
    example: This is a message
  target:
    description: The group/entity to broadcast to
    example: group.downstairs_speakers
  media_content_id:
    description: The media to send
    example: https://ha.xxxxxxxxxx.xx:8123/local/light-chime.wav
  media_content_type:
    description: The type of media
    example: audio/mp3
  stop_display:
    description: Stop the display at the end
    example: true
  volume:
    description: Specify target volume
    example: 0.5
  delay_to_stop:
    description: The Delay to stop the Display
    example: 15
max: 10

I essentially funnel ALL notifications through the script above.

helper scripts:

alias: Media Player Volume Set
sequence:
  - variables:
      quiet: >-
        {% if now() >
        now().replace(hour=20).replace(minute=0).replace(second=0).replace(microsecond=0) 
           or now() < now().replace(hour=8).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          true
        {% else %}
          false
        {% endif %}
      quietfactor: '{% if quiet == "true" %}0.1{% else%}0{% endif %}'
  - choose:
      - conditions:
          - condition: template
            value_template: '{{ entity_id[0].startswith(''input_number'') }}'
            alias: used for manually setting the volume based on the set input number
        sequence:
          - service: media_player.volume_set
            data:
              volume_level: '{{states(entity_id[0])|default(0.5)|float}}'
              entity_id: >-
                {{entity_id[0].replace('input_number.','media_player.').replace('_volume','')
                }}
    default:
      - variables:
          speakers: '{{ entity_id }}'
      - repeat:
          count: '{{ speakers | count }}'
          sequence:
            - variables:
                speaker: '{{ speakers[repeat.index - 1] }}'
            - service: media_player.volume_set
              data:
                volume_level: |-
                  {{( (states(speaker.replace('media_player.','input_number.')
                    ~ '_volume')|default(0.5)|float) - (quietfactor|float) ) | float }}
                entity_id: '{{speaker}}'
mode: restart
fields:
  entity_id:
    description: The entity(s) to broadcast to
    example: media_player.back_office_speaker
alias: Run GA Command
sequence:
  - service: notify.ga_command
    data:
      message: '{{ message }}'
mode: parallel
fields:
  message:
    description: Message to send
    example: This is a message
alias: Media Player Volume Set
sequence:
  - variables:
      quiet: >-
        {% if now() >
        now().replace(hour=20).replace(minute=0).replace(second=0).replace(microsecond=0) 
           or now() < now().replace(hour=8).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          true
        {% else %}
          false
        {% endif %}
      quietfactor: '{% if quiet == "true" %}0.1{% else%}0{% endif %}'
  - choose:
      - conditions:
          - condition: template
            value_template: '{{ entity_id[0].startswith(''input_number'') }}'
            alias: used for manually setting the volume based on the set input number
        sequence:
          - service: media_player.volume_set
            data:
              volume_level: '{{states(entity_id[0])|default(0.5)|float}}'
              entity_id: >-
                {{entity_id[0].replace('input_number.','media_player.').replace('_volume','')
                }}
    default:
      - variables:
          speakers: '{{ entity_id }}'
      - repeat:
          count: '{{ speakers | count }}'
          sequence:
            - variables:
                speaker: '{{ speakers[repeat.index - 1] }}'
            - service: media_player.volume_set
              data:
                volume_level: |-
                  {{( (states(speaker.replace('media_player.','input_number.')
                    ~ '_volume')|default(0.5)|float) - (quietfactor|float) ) | float }}
                entity_id: '{{speaker}}'
mode: restart
fields:
  entity_id:
    description: The entity(s) to broadcast to
    example: media_player.back_office_speaker