diff --git a/homeassistant/components/sonos/const.py b/homeassistant/components/sonos/const.py index 88b71066486..c5a630d73bd 100644 --- a/homeassistant/components/sonos/const.py +++ b/homeassistant/components/sonos/const.py @@ -142,6 +142,7 @@ SONOS_ENTITY_CREATED = "sonos_entity_created" SONOS_POLL_UPDATE = "sonos_poll_update" SONOS_ALARMS_UPDATED = "sonos_alarms_updated" SONOS_FAVORITES_UPDATED = "sonos_favorites_updated" +SONOS_SPEAKER_ADDED = "sonos_speaker_added" SONOS_STATE_UPDATED = "sonos_state_updated" SONOS_REBOOTED = "sonos_rebooted" SONOS_SEEN = "sonos_seen" diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 9485d5dcff3..9febace5e8c 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -50,6 +50,7 @@ from .const import ( SONOS_POLL_UPDATE, SONOS_REBOOTED, SONOS_SEEN, + SONOS_SPEAKER_ADDED, SONOS_STATE_PLAYING, SONOS_STATE_TRANSITIONING, SONOS_STATE_UPDATED, @@ -196,6 +197,7 @@ class SonosSpeaker: self.sonos_group_entities: list[str] = [] self.soco_snapshot: Snapshot | None = None self.snapshot_group: list[SonosSpeaker] | None = None + self._group_members_missing: set[str] = set() def setup(self) -> None: """Run initial setup of the speaker.""" @@ -212,6 +214,11 @@ class SonosSpeaker: self._reboot_dispatcher = dispatcher_connect( self.hass, f"{SONOS_REBOOTED}-{self.soco.uid}", self.async_rebooted ) + self._group_dispatcher = dispatcher_connect( + self.hass, + SONOS_SPEAKER_ADDED, + self.update_group_for_uid, + ) if battery_info := fetch_battery_info_or_none(self.soco): self.battery_info = battery_info @@ -240,6 +247,7 @@ class SonosSpeaker: } dispatcher_send(self.hass, SONOS_CREATE_MEDIA_PLAYER, self) + dispatcher_send(self.hass, SONOS_SPEAKER_ADDED, self.soco.uid) # # Entity management @@ -637,6 +645,16 @@ class SonosSpeaker: """Update group topology when polling.""" self.hass.add_job(self.create_update_groups_coro()) + def update_group_for_uid(self, uid: str) -> None: + """Update group topology if uid is missing.""" + if uid not in self._group_members_missing: + return + missing_zone = self.hass.data[DATA_SONOS].discovered[uid].zone_name + _LOGGER.debug( + "%s was missing, adding to %s group", missing_zone, self.zone_name + ) + self.update_groups() + @callback def async_update_groups(self, event: SonosEvent) -> None: """Handle callback for topology change event.""" @@ -658,7 +676,7 @@ class SonosSpeaker: slave_uids = [ p.uid for p in self.soco.group.members - if p.uid != coordinator_uid + if p.uid != coordinator_uid and p.is_visible ] return [coordinator_uid] + slave_uids @@ -690,11 +708,19 @@ class SonosSpeaker: for uid in group: speaker = self.hass.data[DATA_SONOS].discovered.get(uid) if speaker: + self._group_members_missing.discard(uid) sonos_group.append(speaker) entity_id = entity_registry.async_get_entity_id( MP_DOMAIN, DOMAIN, uid ) sonos_group_entities.append(entity_id) + else: + self._group_members_missing.add(uid) + _LOGGER.debug( + "%s group member unavailable (%s), will try again", + self.zone_name, + uid, + ) if self.sonos_group_entities == sonos_group_entities: # Useful in polling mode for speakers with stereo pairs or surrounds