Fix Sonos races related to grouping and startup (#71026)

This commit is contained in:
jjlawren 2022-04-28 16:26:29 -05:00 committed by GitHub
parent 7e8c6d563f
commit 1f1932d224
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 6 deletions

View File

@ -258,6 +258,15 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
if self.coordinator.uid == uid:
self.async_write_ha_state()
@property
def available(self) -> bool:
"""Return if the media_player is available."""
return (
self.speaker.available
and self.speaker.sonos_group_entities
and self.media.playback_status
)
@property
def coordinator(self) -> SonosSpeaker:
"""Return the current coordinator SonosSpeaker."""

View File

@ -189,7 +189,12 @@ class SonosSpeaker:
def setup(self, entry: ConfigEntry) -> None:
"""Run initial setup of the speaker."""
self.set_basic_info()
self.media.play_mode = self.soco.play_mode
self.update_volume()
self.update_groups()
if self.is_coordinator:
self.media.poll_media()
future = asyncio.run_coroutine_threadsafe(
self.async_setup_dispatchers(entry), self.hass.loop
)
@ -247,11 +252,6 @@ class SonosSpeaker:
"""Write states for associated SonosEntity instances."""
async_dispatcher_send(self.hass, f"{SONOS_STATE_UPDATED}-{self.soco.uid}")
def set_basic_info(self) -> None:
"""Set basic information when speaker is reconnected."""
self.media.play_mode = self.soco.play_mode
self.update_volume()
#
# Properties
#
@ -456,6 +456,34 @@ class SonosSpeaker:
@callback
def async_dispatch_media_update(self, event: SonosEvent) -> None:
"""Update information about currently playing media from an event."""
# The new coordinator can be provided in a media update event but
# before the ZoneGroupState updates. If this happens the playback
# state will be incorrect and should be ignored. Switching to the
# new coordinator will use its media. The regrouping process will
# be completed during the next ZoneGroupState update.
av_transport_uri = event.variables.get("av_transport_uri", "")
current_track_uri = event.variables.get("current_track_uri", "")
if av_transport_uri == current_track_uri and av_transport_uri.startswith(
"x-rincon:"
):
new_coordinator_uid = av_transport_uri.split(":")[-1]
if new_coordinator_speaker := self.hass.data[DATA_SONOS].discovered.get(
new_coordinator_uid
):
_LOGGER.debug(
"Media update coordinator (%s) received for %s",
new_coordinator_speaker.zone_name,
self.zone_name,
)
self.coordinator = new_coordinator_speaker
else:
_LOGGER.debug(
"Media update coordinator (%s) for %s not yet available",
new_coordinator_uid,
self.zone_name,
)
return
if crossfade := event.variables.get("current_crossfade_mode"):
self.cross_fade = bool(int(crossfade))
@ -774,6 +802,7 @@ class SonosSpeaker:
self.zone_name,
uid,
)
return
if self.sonos_group_entities == sonos_group_entities:
# Useful in polling mode for speakers with stereo pairs or surrounds

View File

@ -120,6 +120,7 @@ def soco_fixture(
mock_soco.get_battery_info.return_value = battery_info
mock_soco.all_zones = {mock_soco}
mock_soco.visible_zones = {mock_soco}
mock_soco.group.coordinator = mock_soco
yield mock_soco