Reorganize SonosSpeaker class for readability (#51222)
parent
27b9d7fed0
commit
3d2f696d73
|
@ -146,36 +146,43 @@ class SonosSpeaker:
|
|||
self.household_id: str = soco.household_id
|
||||
self.media = SonosMedia(soco)
|
||||
|
||||
# Synchronization helpers
|
||||
self.is_first_poll: bool = True
|
||||
self._is_ready: bool = False
|
||||
self._platforms_ready: set[str] = set()
|
||||
|
||||
# Subscriptions and events
|
||||
self._subscriptions: list[SubscriptionBase] = []
|
||||
self._resubscription_lock: asyncio.Lock | None = None
|
||||
self._event_dispatchers: dict[str, Callable] = {}
|
||||
|
||||
# Scheduled callback handles
|
||||
self._poll_timer: Callable | None = None
|
||||
self._seen_timer: Callable | None = None
|
||||
self._platforms_ready: set[str] = set()
|
||||
|
||||
# Dispatcher handles
|
||||
self._entity_creation_dispatcher: Callable | None = None
|
||||
self._group_dispatcher: Callable | None = None
|
||||
self._seen_dispatcher: Callable | None = None
|
||||
|
||||
# Device information
|
||||
self.mac_address = speaker_info["mac_address"]
|
||||
self.model_name = speaker_info["model_name"]
|
||||
self.version = speaker_info["display_version"]
|
||||
self.zone_name = speaker_info["zone_name"]
|
||||
|
||||
# Battery
|
||||
self.battery_info: dict[str, Any] | None = None
|
||||
self._last_battery_event: datetime.datetime | None = None
|
||||
self._battery_poll_timer: Callable | None = None
|
||||
|
||||
# Volume / Sound
|
||||
self.volume: int | None = None
|
||||
self.muted: bool | None = None
|
||||
self.night_mode: bool | None = None
|
||||
self.dialog_mode: bool | None = None
|
||||
|
||||
# Grouping
|
||||
self.coordinator: SonosSpeaker | None = None
|
||||
self.sonos_group: list[SonosSpeaker] = [self]
|
||||
self.sonos_group_entities: list[str] = []
|
||||
|
@ -232,6 +239,9 @@ class SonosSpeaker:
|
|||
|
||||
dispatcher_send(self.hass, SONOS_CREATE_MEDIA_PLAYER, self)
|
||||
|
||||
#
|
||||
# Entity management
|
||||
#
|
||||
async def async_handle_new_entity(self, entity_type: str) -> None:
|
||||
"""Listen to new entities to trigger first subscription."""
|
||||
self._platforms_ready.add(entity_type)
|
||||
|
@ -254,11 +264,32 @@ class SonosSpeaker:
|
|||
self.media.play_mode = self.soco.play_mode
|
||||
self.update_volume()
|
||||
|
||||
#
|
||||
# Properties
|
||||
#
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return whether this speaker is available."""
|
||||
return self._seen_timer is not None
|
||||
|
||||
@property
|
||||
def favorites(self) -> SonosFavorites:
|
||||
"""Return the SonosFavorites instance for this household."""
|
||||
return self.hass.data[DATA_SONOS].favorites[self.household_id]
|
||||
|
||||
@property
|
||||
def is_coordinator(self) -> bool:
|
||||
"""Return true if player is a coordinator."""
|
||||
return self.coordinator is None
|
||||
|
||||
@property
|
||||
def subscription_address(self) -> str | None:
|
||||
"""Return the current subscription callback address if any."""
|
||||
if self._subscriptions:
|
||||
addr, port = self._subscriptions[0].event_listener.address
|
||||
return ":".join([addr, str(port)])
|
||||
return None
|
||||
|
||||
#
|
||||
# Subscription handling and event dispatchers
|
||||
#
|
||||
|
@ -295,6 +326,30 @@ class SonosSpeaker:
|
|||
subscription.auto_renew_fail = self.async_renew_failed
|
||||
self._subscriptions.append(subscription)
|
||||
|
||||
@callback
|
||||
def async_renew_failed(self, exception: Exception) -> None:
|
||||
"""Handle a failed subscription renewal."""
|
||||
self.hass.async_create_task(self.async_resubscribe(exception))
|
||||
|
||||
async def async_resubscribe(self, exception: Exception) -> None:
|
||||
"""Attempt to resubscribe when a renewal failure is detected."""
|
||||
async with self._resubscription_lock:
|
||||
if not self.available:
|
||||
return
|
||||
|
||||
if getattr(exception, "status", None) == 412:
|
||||
_LOGGER.warning(
|
||||
"Subscriptions for %s failed, speaker may have lost power",
|
||||
self.zone_name,
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Subscription renewals for %s failed",
|
||||
self.zone_name,
|
||||
exc_info=exception,
|
||||
)
|
||||
await self.async_unseen()
|
||||
|
||||
@callback
|
||||
def async_dispatch_event(self, event: SonosEvent) -> None:
|
||||
"""Handle callback event and route as needed."""
|
||||
|
@ -349,6 +404,9 @@ class SonosSpeaker:
|
|||
|
||||
self.async_write_entity_states()
|
||||
|
||||
#
|
||||
# Speaker availability methods
|
||||
#
|
||||
async def async_seen(self, soco: SoCo | None = None) -> None:
|
||||
"""Record that this speaker was seen right now."""
|
||||
if soco is not None:
|
||||
|
@ -386,28 +444,6 @@ class SonosSpeaker:
|
|||
|
||||
self.async_write_entity_states()
|
||||
|
||||
async def async_resubscribe(self, exception: Exception) -> None:
|
||||
"""Attempt to resubscribe when a renewal failure is detected."""
|
||||
async with self._resubscription_lock:
|
||||
if self.available:
|
||||
if getattr(exception, "status", None) == 412:
|
||||
_LOGGER.warning(
|
||||
"Subscriptions for %s failed, speaker may have lost power",
|
||||
self.zone_name,
|
||||
)
|
||||
else:
|
||||
_LOGGER.error(
|
||||
"Subscription renewals for %s failed",
|
||||
self.zone_name,
|
||||
exc_info=exception,
|
||||
)
|
||||
await self.async_unseen()
|
||||
|
||||
@callback
|
||||
def async_renew_failed(self, exception: Exception) -> None:
|
||||
"""Handle a failed subscription renewal."""
|
||||
self.hass.async_create_task(self.async_resubscribe(exception))
|
||||
|
||||
async def async_unseen(self, now: datetime.datetime | None = None) -> None:
|
||||
"""Make this player unavailable when it was not seen recently."""
|
||||
self.async_write_entity_states()
|
||||
|
@ -425,6 +461,9 @@ class SonosSpeaker:
|
|||
|
||||
self._subscriptions = []
|
||||
|
||||
#
|
||||
# Alarm management
|
||||
#
|
||||
def update_alarms_for_speaker(self) -> set[str]:
|
||||
"""Update current alarm instances.
|
||||
|
||||
|
@ -453,6 +492,9 @@ class SonosSpeaker:
|
|||
dispatcher_send(self.hass, SONOS_CREATE_ALARM, self, new_alarms)
|
||||
dispatcher_send(self.hass, SONOS_ALARM_UPDATE)
|
||||
|
||||
#
|
||||
# Battery management
|
||||
#
|
||||
async def async_update_battery_info(self, battery_dict: dict[str, Any]) -> None:
|
||||
"""Update battery info using the decoded SonosEvent."""
|
||||
self._last_battery_event = dt_util.utcnow()
|
||||
|
@ -477,11 +519,6 @@ class SonosSpeaker:
|
|||
):
|
||||
self.battery_info = battery_info
|
||||
|
||||
@property
|
||||
def is_coordinator(self) -> bool:
|
||||
"""Return true if player is a coordinator."""
|
||||
return self.coordinator is None
|
||||
|
||||
@property
|
||||
def power_source(self) -> str | None:
|
||||
"""Return the name of the current power source.
|
||||
|
@ -516,6 +553,9 @@ class SonosSpeaker:
|
|||
self.battery_info = battery_info
|
||||
self.async_write_entity_states()
|
||||
|
||||
#
|
||||
# Group management
|
||||
#
|
||||
def update_groups(self, event: SonosEvent | None = None) -> None:
|
||||
"""Handle callback for topology change event."""
|
||||
coro = self.create_update_groups_coro(event)
|
||||
|
@ -786,11 +826,9 @@ class SonosSpeaker:
|
|||
for speaker in hass.data[DATA_SONOS].discovered.values():
|
||||
speaker.soco._zgs_cache.clear() # pylint: disable=protected-access
|
||||
|
||||
@property
|
||||
def favorites(self) -> SonosFavorites:
|
||||
"""Return the SonosFavorites instance for this household."""
|
||||
return self.hass.data[DATA_SONOS].favorites[self.household_id]
|
||||
|
||||
#
|
||||
# Media and playback state handlers
|
||||
#
|
||||
def update_volume(self) -> None:
|
||||
"""Update information about current volume settings."""
|
||||
self.volume = self.soco.volume
|
||||
|
@ -951,11 +989,3 @@ class SonosSpeaker:
|
|||
elif update_media_position:
|
||||
self.media.position = current_position
|
||||
self.media.position_updated_at = dt_util.utcnow()
|
||||
|
||||
@property
|
||||
def subscription_address(self) -> str | None:
|
||||
"""Return the current subscription callback address if any."""
|
||||
if self._subscriptions:
|
||||
addr, port = self._subscriptions[0].event_listener.address
|
||||
return ":".join([addr, str(port)])
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue