Sure there is. I use a script for that. I will post it later. Here you go: scripts.yaml:
announce_downstairs:
alias: 'Announce downstairs'
sequence:
- service: media_player.volume_mute # The first part gets rid of the default Google Home bloop-bloop sound. First mutes the speaker...
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: true
- service: media_player.play_media # then plays a random 1 second silent track ...
data:
entity_id: media_player.googlehome_downstairs
media_content_type: music
media_content_id: "{{ states('input_text.base_url') + '/local/audio/1sec.mp3' }}"
- delay:
seconds: 1
- service: media_player.volume_set # then sets the desired volume ...
data:
entity_id: media_player.googlehome_downstairs
volume_level: >-
{% if now().hour > 18 or now().hour < 7 %} 0.35
{% else %} 0.65
{% endif %}
- service: media_player.volume_mute # only then unmutes, so you don't hear the volume changing sounds
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: false
- service: media_player.play_media # then it plays a nice ding-dong sound
data:
entity_id: media_player.googlehome_downstairs
media_content_type: music
media_content_id: "{{ states('input_text.base_url') + '/local/audio/announcement.mp3' }}"
# My base_url is contained in an input_text, so that I could put it in secrets.yaml. You can just put the external url to your HA frontend here. NB an internal url won't work in my experience.
- delay:
seconds: 2
# milliseconds: 800
- service: tts.google_say # then finally here's your TTS message :-)
data:
entity_id: media_player.googlehome_downstairs
message: "{{my_message}}"
Calling the script from an automation:
- service: script.announce_downstairs
data:
my_message: "Have a great day"
I see you have volume-setting capabilities in your script, but I don’t see anything that saves the previous volume setting and restores it after the announcement. Have you figured out a way to acquire the current volume setting before you make changes? Restoring should be pretty simple after that.
Here’s an adaptation that also restores the volume to its original volume, after the announcement.
announce_downstairs:
alias: 'Announce downstairs'
sequence:
- service: media_player.volume_mute # The first part gets rid of the default Google Home bloop-bloop sound. First mutes the speaker...
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: true
- service: media_player.play_media # then plays a random 1 second silent track ...
data:
entity_id: media_player.googlehome_downstairs
media_content_type: music
media_content_id: "{{ states('input_text.base_url') + '/local/audio/1sec.mp3' }}"
- delay:
seconds: 1
- service: input_number.set_value # store the current volume level (should be after playing 1sec because only then the volume_level attribute is available)
data:
entity_id: input_number.current_volume
value: "{{ state_attr('media_player.googlehome_downstairs','volume_level') }}"
- service: media_player.volume_set # then sets the desired volume for the announcement ...
data:
entity_id: media_player.googlehome_downstairs
volume_level: >-
{% if now().hour > 18 or now().hour < 7 %} 0.35
{% else %} 0.65
{% endif %}
- service: media_player.volume_mute # only then unmutes, so you don't hear the volume changing sounds
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: false
- service: media_player.play_media # then it plays a nice ding-dong sound
data:
entity_id: media_player.googlehome_downstairs
media_content_type: music
media_content_id: "{{ states('input_text.base_url') + '/local/audio/announcement.mp3' }}"
# My base_url is contained in an input_text, so that I could put it in secrets.yaml. You can just put the external url to your HA frontend here. NB an internal url won't work in my experience.
- delay:
seconds: 2
# milliseconds: 800
- service: tts.google_say # then finally here's your TTS message :-)
data:
entity_id: media_player.googlehome_downstairs
message: "{{my_message}}"
- delay: # Set this delay for the maximum length of your announcement messages
seconds: 4
- service: media_player.volume_mute # Again, muting to prevent volume changing sounds
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: true
- service: media_player.volume_set
data:
entity_id: media_player.googlehome_downstairs
volume_level: "{{ states('input_number.current_volume') }}"
- delay:
seconds: 1
- service: media_player.volume_mute
data:
entity_id: media_player.googlehome_downstairs
is_volume_muted: false
This requires an input_number to be defined in configuration.yaml:
I took @Emphyrio’s example above and parameterized it to make it easier to use generically - i.e. use any media_player entity (or several!), use a specificed volume, etc.
First, a setup script:
# Turns on Google smart speaker(s), bypassing the Google chime by muting
#
# Args:
# announcement_volume (float): speaker volume_level for announcement (0 <= x <= 1)
# player_id (entity or list of entities): media_player(s) on which to play announcement
#
# Usage Example:
# - service: script.google_speaker_announcement_startup
# data:
# announcement_volume: "{{ states('input_number.volume_announcement') }}"
# player_id: media_player.office_display, media_player.family_room_speaker
google_speaker_announcement_startup:
alias: 'GoogleSpeakerAnnouncementStartup'
sequence:
- service: media_player.volume_mute
data:
entity_id: "{{ player_id }}"
is_volume_muted: true
- delay:
milliseconds: 200
- service: media_player.play_media
data:
entity_id: "{{ player_id }}"
media_content_type: music
media_content_id: "{{ states('input_text.home_assistant_url_internal') + '/local/sounds/silence-1sec.mp3' }}"
- delay:
seconds: 1
- service: media_player.volume_set
data_template:
entity_id: "{{ player_id }}"
volume_level: "{{ announcement_volume }}"
- delay:
seconds: 2
- service: media_player.volume_mute
data:
entity_id: "{{ player_id }}"
is_volume_muted: false
- service: media_player.play_media
data:
entity_id: "{{ player_id }}"
media_content_type: music
media_content_id: "{{ states('input_text.home_assistant_url_internal') + '/local/sounds/announcement_chime.mp3' }}"
- delay:
seconds: 3
Note that I’ve found this eliminates the Google chime most of the time, but not all of the time. I played with lots of variations of this sequence in terms of order, timing, etc., and haven’t gotten it to be silent 100% of the time. But this one is close…
Then there’s a shutdown script:
# Turns off Google smart speaker(s), restoring volume to default
#
# Args:
# player_id (entity or list of entities): media_player(s) to shut down
#
# Usage Example:
# - service: script.google_speaker_announcement_shutdown
# data:
# player_id: media_player.office_display, media_player.family_room_speaker
google_speaker_announcement_shutdown:
alias: 'GoogleSpeakerAnnouncementShutdown'
sequence:
- service: media_player.volume_set
data_template:
entity_id: "{{ player_id }}"
volume_level: "{{ states('input_number.volume_smart_speaker_default') }}"
- delay:
seconds: 1
- service: media_player.turn_off
data:
entity_id: "{{ player_id }}"
And finally, there’s the announcement itself:
# Plays an TTS announcement on Google smart speaker(s) with specificed volume
#
# Args:
# announcement_length (int): number of seconds to delay after starting announcement
# before assuming it has completed
# announcement_message (string): message used as input to text-to-speech
# announcement_volume (float): speaker volume_level for announcement (0 <= x <= 1)
# player_id (entity or list of entities): media_player(s) on which to play announcement
#
# Usage Example:
# - service: script.announce
# data:
# announcement_length: 10
# announcement_message: "This is a test. This is only a test of the Home Asssistant alert system."
# announcement_volume: "{{ states('input_number.volume_announcement') }}"
# player_id: media_player.office_display, media_player.family_room_speaker
announcement:
alias: 'Announcement'
sequence:
- service: script.google_speaker_announcement_startup
data:
player_id: "{{ player_id }}"
announcement_volume: "{{ announcement_volume}}"
- service: tts.cloud_say
data:
entity_id: "{{ player_id }}"
message: "{{ announcement_message }}"
- delay:
seconds: "{{ announcement_length }}"
- service: script.google_speaker_announcement_shutdown
data:
player_id: "{{ player_id }}"
So to use this, you just call the script.announce from an automation or other script, as shown in the example above.
Also, to address @Jiran’s original question (how to restore the volume to its previous level after the announcement), I tried this:
announcement_with_volume_restore:
alias: 'AnnouncementWithVolumeRestore'
variables:
previous_volume: >
"{% if is_state('media_player.office_display', 'playing')
or is_state('media_player.office_display', 'on')
or is_state('media_player.office_display', 'paused')
%}
{{ state_attr('media_player.office_display', 'volume_level') | float | round(2) }}
{% else %}
{{ states('input_number.volume_smart_speaker_default') | float | round(2) }}
{% endif %}"
sequence:
- service: script.google_speaker_announcement_startup
data:
player_id: "{{ player_id }}"
announcement_volume: "{{ announcement_volume}}"
- service: tts.cloud_say
data:
entity_id: "{{ player_id }}"
message: "{{ announcement_message }}"
- delay:
seconds: 10
- service: media_player.volume_set
data_template:
entity_id: "{{ player_id }}"
volume_level: "{{ previous_volume }}" # doesn't seem to be a float, and casting as float makes it 0
- service: media_player.turn_off
data:
entity_id: "{{ player_id }}"
That example has the entity for the previous volume hardcoded to a specific media player since I was experimenting. If the device is off (not playing), the above works by restoring the value of volume_smart_speaker_default to the device after the announcement.
In the case of the device being originally in the playing state, the value of previous_volume is saved and able to be read back with a second TTS call. However, when trying to restore the volume (line with comment above), it complains about it not being a float. If I cast it as a float (add | float), then the volume is restored as a value of zero.
I’ve tried a bunch of stuff to get this part to work, but haven’t been successful. Other ideas welcome!
I read your post above as setting up a static input_number to have as the restored value and missed that you were calling the service to set it within the script. My bad.
That works as long as you don’t have multiple announcements going at the same time… which I’m guessing for most people would be rare.
Any thoughts on how to do it without adding an extra input_number to keep things simpler?
Fantastic post and love the solution by you, @Emphyrio. I also tried to implement a minimal setup as a script, however, when running it, I do script/automation that calls it does not turn back the volume. Here is what I implemented:
My guess would be it has something to do with the delays. After sending the TTS command, it takes some time to process (generate the audio file, send the command to the speaker, download & play the audio file). If you send a new command in the meantime, like setting the volume, it might interfere.
So I would add some bigger delays after your tts.google_say commands.
You should be posting the error you’re getting in the log to help people diagnose your issue. In this case the problem is easy to spot. You’re missing some quotes. Volume level parameter also requires a float value.
Thanks, @jazzyisj & @Emphyrio. Indeed it was a combination of issues - the quotes were missing, I had to do a cast to float, and a major issue was that the variable was never defined. Making use fo the log certainly helps here … beginner’s fault
Is there a way to define a variable within a script locally so that it only exists there, that there are no conflicts with potentially equally named variables in other scripts, and to make sure that it is always defined for that script independent of any “global” setups? I am not referring to variables that should be passed to that script, but indeed only ones to be used within the script, like the current volume one here.
Thanks, Jason. I am still stuggling (as a newbie) with the basics of setting a specific value to a variable. As some code snippets, I would have assumed that it works like the following, but obviously some major flaws in there as it doesn’t even recognize the service:
Would anybody be able to point me to the (probably obvious) mistakes here and how to handle such variables? Unfortunately the “Scripts” link doesn’t explain that and also some additional searching hasn’t led to anything. Thanks again!
Looks like you are mixing up automation/script variables and one of the custom variable integrations. Where did you come up with the set_variable service you tried to use? You also didn’t name your script!
You really need to read the link I sent before and this link carefully. If you’re trying to write scripts and don’t understand the basic format you’re going to be in for a very frustrating time!
Once you’ve read those if you still have questions, fire away…
Here’s a quick example to demonstrate how to change a variable value within a script. Put this in your config, reload your scripts, execute this script, then look at your log to see the result.