Confirm Sonos announcements from the speaker's clip status (0.3.0) - #7
Merged
Merged
Conversation
added 6 commits
September 17, 2026 00:06
Playback confirmation was a single hard-coded entity-state watch inside broadcast.py. Move it behind a small verifier API so players that expose better evidence can get their own check: - PlaybackVerifier: base class that checks nothing and assumes the clip played (used when verify is off). Subclasses register themselves and must define name and matches(hass, entity_id); priority breaks ties. - StateVerifier: the existing state watch, unchanged in behaviour, and the fallback for every player. - Verdicts are started / failed / timeout / assumed, so a verifier can now report that the player itself refused the clip (status failed). - Each player outcome gains verified_by, naming the check that judged it. Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
Home Assistant's Sonos integration plays announcements (so tts.speak) through the speaker's audio-clip API, and the media player entity never changes state while a clip plays. Every Sonos broadcast was therefore reported as unverified, and the whole broadcast as FAILED, even when it was clearly heard. Add SonosClipVerifier. It finds the speaker behind the entity through the entity and device registries (native Sonos entities, Music Assistant players that carry the Sonos player id, and Music Assistant players that wrap a native entity), opens its own connection to the speaker's local websocket API, subscribes to audio-clip status and follows the first clip that appears after it was armed: - ACTIVE / DONE: played - ERROR: failed, with a message pointing at the usual cause (the speaker cannot reach the address Home Assistant gave it for the audio) While that connection is up it is authoritative, since a proxy entity can look busy while the speaker refuses the clip. If it cannot be opened, or drops, the ordinary entity-state check decides instead. Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
Add docs/troubleshooting.md covering a speaker that accepts a broadcast but cannot fetch the audio (a multi-homed Home Assistant host handing out an address the speaker cannot reach, how to confirm it and how to fix it), broadcasts reported failed but heard, and where to find the logs. README: document verified_by and the sonos_clip / state checks, how to add a check for another kind of player, and that failed now also covers a speaker refusing the clip. Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
Audio can reach a Sonos without going through its audio-clip API, e.g. Music Assistant streaming over AirPlay. The clip channel then stays silent, and holding back the entity-state evidence would report such broadcasts as unverified. If no clip has appeared by the start timeout, use the state evidence after all. Once the speaker has shown our clip, its status stays authoritative. Also document that targeting one Sonos through two entities in a single broadcast is ambiguous. Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
Where to find an add-on's log is a Home Assistant question, not one about this integration. Keep only the part that is: a speaker's own errors are reported to whatever asked it to play, so they surface in that component's log. Helped-By: claude-opus-5 <anthropic@willroscoe.uk>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Broadcasts to a Sonos speaker were reported as
unverified, with the summary readingFAILED — played on 0/1 speakers, even when the message was clearly heard.Home Assistant's Sonos integration plays announcements (so
tts.speak) through the speaker's local audio-clip API, and the media player entity does not change state while a clip plays. The intercom confirmed playback purely from entity state, so it could never see a Sonos announcement. That applied to the native Sonos entity and to Music Assistant players backed by the same speaker.Change
A pluggable playback check (
custom_components/intercom/verifiers/):PlaybackVerifier: the base class. It checks nothing and assumes the clip played, and is used whenverify: false. Subclasses register themselves when defined, and must definenameandmatches(hass, entity_id).prioritydecides between several matches.StateVerifier: the existing entity-state check, moved unchanged. It is the fallback for every player.started/failed/timeout/assumed), so a player that itself refuses a clip is reported asfailed, notunverified.verified_by, naming the check that judged it.SonosClipVerifier(sonos_clip):audioClip:1status, ignores clips the speaker already had, and follows the first new clip.ACTIVE/DONEmeans played;ERRORmeans failed, with a message pointing at the usual cause (the speaker cannot reach the audio link Home Assistant gave it).aiohttpandsonos_websocket(which ships with HA's Sonos integration) are imported only when a Sonos speaker is actually checked.Docs: new
docs/troubleshooting.md, and README notes onverified_by, the two checks, how to add one, and the limitations. The troubleshooting guide covers the case that exposed this: a Home Assistant host on two subnets hands out an audio link on the one the speaker cannot reach. The speaker accepts the command and plays nothing, and Music Assistant showsERROR_LOST_CONNECTION.Version bumped to 0.3.0.
Testing
79 tests pass (37 new),
ruff checkandruff format --checkclean.unverified, as the bug did.Live, against a Sonos Roam on Home Assistant, running this module's clip channel without restarting HA:
tts.speakvia the native entityACTIVE→DONEtts.speakvia the Music Assistant entityACTIVE→DONEERRORafter ~3 sAddress lookup against the real registries: the native entity and both Music Assistant entities resolve to the speaker, and none of the other 7 media players match.