Whole home announcements with Sonos speakers, Home Assistant and Chime TTS
For smart home enthusiasts, having a synchronized announcement system throughout your home can be transformative. In this post, I'll share how to create a home-wide announcement system using Home Assistant, Sonos speakers, and Chime TTS that respects your daily routines and preferences.
A well-implemented home announcement system should seamlessly integrate with your lifestyle, providing timely information without disrupting important moments. While Sonos speakers offer excellent audio quality and multi-room capabilities, creating a synchronized announcement system that gracefully handles different scenarios requires some planning and trial and error.
Goals
The primary objective was to create an announcement system that feels natural and non-intrusive. This meant ensuring announcements play simultaneously across all appropriate speakers, temporarily pause any current audio, and resume playback afterward. Most importantly, the system needed to be smart enough to avoid disrupting spaces where announcements would be unwelcome, such as during sleep or yoga sessions.
Challenges
Creating this system presented several technical hurdles. The native Sonos announce feature, while useful for single-speaker scenarios, doesn't support synchronized playback across speaker groups. Meanwhile, simply grouping speakers using Home Assistant's media_player.join
and media_player.unjoin
functions disrupted ongoing playback without properly restoring the previous state.
Additionally, with multiple automations triggering announcements, there was a risk of code duplication across automation configurations making it difficult to maintain. The system also needed to be easily testable to ensure reliability.
Approach
The solution centered around creating a centralized script that handles all announcement logic. Here's how it works:
First, the script evaluates various conditions to determine if announcements are appropriate:
- Checks if sleep mode is not active
- Verifies the time is between 7 AM and 11 PM
- Confirms announcements aren't disabled
- Checks bed presence sensors are unoccupied
- condition: state
entity_id: input_boolean.sleep_mode
state: "off"
for:
hours: 0
minutes: 0
seconds: 0
- condition: state
entity_id: input_boolean.disable_announcements
state: "off"
for:
hours: 0
minutes: 0
seconds: 0
- condition: state
entity_id: binary_sensor.someone_sleeping
state: "off"
for:
hours: 0
minutes: 0
seconds: 0
- condition: state
entity_id: binary_sensor.shymoose_occupancy
state: "on"
for:
hours: 0
minutes: 0
seconds: 0
- condition: time
after: "07:30:00"
before: "23:00:00"
If any of these conditions are not met, then script does not proceed
Next, it dynamically builds a list of target speakers based on current conditions (and defines a default message, and determine whether or not any of the speakers are playing anything already):
- Starts with all available speakers
- Excludes bedroom speakers during sleep mode
- Removes the gym speaker during yoga sessions
- variables:
default_message: >-
{{ ["OK", "Sure", "If you insist", "Done", "Why not", "Got it", "Here
you go", "Alright"] | random }}, this is a notification test. It is
slightly longer to make sure things are in sync. Yoga mode is {{
states('timer.yoga_mode') }}, Sleep mode is {{
states('input_boolean.sleep_mode') }}. {{ ["Meow", "Woof", "Woof woof"]
| random }}.
target_speakers: |-
{% set speakers = [
'media_player.gym_sonos', 'media_player.family_sonos',
'media_player.bedroom_sonos', 'media_player.kitchen_sonos',
'media_player.her_office_sonos', 'media_player.his_office_sonos'
] %} {% if is_state('timer.yoga_mode', 'active') %}
{{ speakers | reject('eq', 'media_player.gym_sonos') | list }}
{% elif is_state('input_boolean.sleep_mode', 'on') %}
{{ speakers | reject('eq', 'media_player.bedroom_sonos') | list }}
{% else %}
{{ speakers }}
{% endif %}
is_speaker_in_use: >-
{{ 'true' if target_speakers | select('is_state', 'playing') | list |
length > 0 else 'false' }}
The last check branches the sequence into two paths:
If speakers are active:
- Takes a snapshot of the current audio state
- Plays the announcement using Chime TTS
- Restores the previous audio state
If no speakers are active:
- Proceeds directly to playing the announcement
- No restoration needed
The Chime TTS action accepts a variable called message
from the triggering automation but includes a default message defined earlier for testing purposes.
- sequence:
- alias: Run Sonos snapshot if speakers are already playing something
if:
- condition: template
value_template: "{{ is_speaker_in_use == 'true' }}"
alias: Target speakers are in use
then:
- action: sonos.snapshot
metadata: {}
data:
with_group: true
entity_id: all
- action: chime_tts.say
data:
message: "{{ message | default(default_message, true)}}"
tts_platform: microsoft
chime_path: custom_chime_path_5
join_players: true
unjoin_players: true
cache: true
volume_level: 15
final_delay: 100
offset: 300
target:
entity_id: "{{ target_speakers }}"
- alias: Run Sonos restore if speakers were already playing something
if:
- condition: template
value_template: "{{ is_speaker_in_use == 'true' }}"
alias: Target speakers were in use
then:
- delay:
hours: 0
minutes: 0
seconds: 1
milliseconds: 0
- action: sonos.restore
metadata: {}
data:
with_group: true
entity_id: all
The sonos.snapshot and sonos.restore are only ran if speakers were already in-use
Summary
This implementation provides a robust, centralized solution for whole-home announcements that respects your daily routines and activities. By carefully managing speaker states and providing flexible condition checking, the system delivers announcements exactly where and when they're appropriate.
The modular approach makes it easy to maintain and modify the system as needs change, while the testing capabilities ensure reliability. This solution demonstrates how thoughtful home automation can enhance daily life without becoming intrusive.