Fix Sonos races related to grouping and startup (#71026)
This commit is contained in:
parent
7e8c6d563f
commit
1f1932d224
3 changed files with 45 additions and 6 deletions
|
@ -258,6 +258,15 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||||
if self.coordinator.uid == uid:
|
if self.coordinator.uid == uid:
|
||||||
self.async_write_ha_state()
|
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
|
@property
|
||||||
def coordinator(self) -> SonosSpeaker:
|
def coordinator(self) -> SonosSpeaker:
|
||||||
"""Return the current coordinator SonosSpeaker."""
|
"""Return the current coordinator SonosSpeaker."""
|
||||||
|
|
|
@ -189,7 +189,12 @@ class SonosSpeaker:
|
||||||
|
|
||||||
def setup(self, entry: ConfigEntry) -> None:
|
def setup(self, entry: ConfigEntry) -> None:
|
||||||
"""Run initial setup of the speaker."""
|
"""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(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
self.async_setup_dispatchers(entry), self.hass.loop
|
self.async_setup_dispatchers(entry), self.hass.loop
|
||||||
)
|
)
|
||||||
|
@ -247,11 +252,6 @@ class SonosSpeaker:
|
||||||
"""Write states for associated SonosEntity instances."""
|
"""Write states for associated SonosEntity instances."""
|
||||||
async_dispatcher_send(self.hass, f"{SONOS_STATE_UPDATED}-{self.soco.uid}")
|
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
|
# Properties
|
||||||
#
|
#
|
||||||
|
@ -456,6 +456,34 @@ class SonosSpeaker:
|
||||||
@callback
|
@callback
|
||||||
def async_dispatch_media_update(self, event: SonosEvent) -> None:
|
def async_dispatch_media_update(self, event: SonosEvent) -> None:
|
||||||
"""Update information about currently playing media from an event."""
|
"""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"):
|
if crossfade := event.variables.get("current_crossfade_mode"):
|
||||||
self.cross_fade = bool(int(crossfade))
|
self.cross_fade = bool(int(crossfade))
|
||||||
|
|
||||||
|
@ -774,6 +802,7 @@ class SonosSpeaker:
|
||||||
self.zone_name,
|
self.zone_name,
|
||||||
uid,
|
uid,
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if self.sonos_group_entities == sonos_group_entities:
|
if self.sonos_group_entities == sonos_group_entities:
|
||||||
# Useful in polling mode for speakers with stereo pairs or surrounds
|
# Useful in polling mode for speakers with stereo pairs or surrounds
|
||||||
|
|
|
@ -120,6 +120,7 @@ def soco_fixture(
|
||||||
mock_soco.get_battery_info.return_value = battery_info
|
mock_soco.get_battery_info.return_value = battery_info
|
||||||
mock_soco.all_zones = {mock_soco}
|
mock_soco.all_zones = {mock_soco}
|
||||||
mock_soco.visible_zones = {mock_soco}
|
mock_soco.visible_zones = {mock_soco}
|
||||||
|
mock_soco.group.coordinator = mock_soco
|
||||||
yield mock_soco
|
yield mock_soco
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue