Avoid multiple context switches to setup a sonos speaker (#113378)

This commit is contained in:
J. Nick Koston 2024-03-15 01:03:44 -10:00 committed by GitHub
parent 103b8b4dc5
commit 45195d2ea9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -31,7 +31,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
dispatcher_send,
)
from homeassistant.helpers.event import async_track_time_interval, track_time_interval
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.util import dt as dt_util
from .alarms import SonosAlarms
@ -127,7 +127,6 @@ class SonosSpeaker:
zone_group_state_sub.callback = self.async_dispatch_event
self._subscriptions.append(zone_group_state_sub)
self._subscription_lock: asyncio.Lock | None = None
self._event_dispatchers: dict[str, Callable] = {}
self._last_activity: float = NEVER_TIME
self._last_event_cache: dict[str, Any] = {}
self.activity_stats: ActivityStatistics = ActivityStatistics(self.zone_name)
@ -179,13 +178,22 @@ class SonosSpeaker:
self.snapshot_group: list[SonosSpeaker] = []
self._group_members_missing: set[str] = set()
async def async_setup(self, entry: ConfigEntry) -> None:
async def async_setup(
self, entry: ConfigEntry, has_battery: bool, dispatches: list[tuple[Any, ...]]
) -> None:
"""Complete setup in async context."""
# Battery events can be infrequent, polling is still necessary
if has_battery:
self._battery_poll_timer = async_track_time_interval(
self.hass, self.async_poll_battery, BATTERY_SCAN_INTERVAL
)
self.websocket = SonosWebsocket(
self.soco.ip_address,
player_id=self.soco.uid,
session=async_get_clientsession(self.hass),
)
dispatch_pairs: tuple[tuple[str, Callable[..., Any]], ...] = (
(SONOS_CHECK_ACTIVITY, self.async_check_activity),
(SONOS_SPEAKER_ADDED, self.async_update_group_for_uid),
@ -203,6 +211,11 @@ class SonosSpeaker:
)
)
for dispatch in dispatches:
async_dispatcher_send(self.hass, *dispatch)
await self.async_subscribe()
def setup(self, entry: ConfigEntry) -> None:
"""Run initial setup of the speaker."""
self.media.play_mode = self.soco.play_mode
@ -211,53 +224,34 @@ class SonosSpeaker:
if self.is_coordinator:
self.media.poll_media()
future = asyncio.run_coroutine_threadsafe(
self.async_setup(entry), self.hass.loop
)
future.result(timeout=10)
dispatcher_send(self.hass, SONOS_CREATE_LEVELS, self)
dispatches: list[tuple[Any, ...]] = [(SONOS_CREATE_LEVELS, self)]
if audio_format := self.soco.soundbar_audio_input_format:
dispatcher_send(
self.hass, SONOS_CREATE_AUDIO_FORMAT_SENSOR, self, audio_format
)
dispatches.append((SONOS_CREATE_AUDIO_FORMAT_SENSOR, self, audio_format))
has_battery = False
try:
self.battery_info = self.fetch_battery_info()
except SonosUpdateError:
_LOGGER.debug("No battery available for %s", self.zone_name)
else:
# Battery events can be infrequent, polling is still necessary
self._battery_poll_timer = track_time_interval(
self.hass, self.async_poll_battery, BATTERY_SCAN_INTERVAL
)
has_battery = True
dispatcher_send(self.hass, SONOS_CREATE_BATTERY, self)
if (mic_enabled := self.soco.mic_enabled) is not None:
self.mic_enabled = mic_enabled
dispatcher_send(self.hass, SONOS_CREATE_MIC_SENSOR, self)
dispatches.append((SONOS_CREATE_MIC_SENSOR, self))
if new_alarms := [
alarm.alarm_id for alarm in self.alarms if alarm.zone.uid == self.soco.uid
]:
dispatcher_send(self.hass, SONOS_CREATE_ALARM, self, new_alarms)
dispatches.append((SONOS_CREATE_ALARM, self, new_alarms))
dispatcher_send(self.hass, SONOS_CREATE_SWITCHES, self)
dispatches.append((SONOS_CREATE_SWITCHES, self))
dispatches.append((SONOS_CREATE_MEDIA_PLAYER, self))
dispatches.append((SONOS_SPEAKER_ADDED, self.soco.uid))
self._event_dispatchers = {
"AlarmClock": self.async_dispatch_alarms,
"AVTransport": self.async_dispatch_media_update,
"ContentDirectory": self.async_dispatch_favorites,
"DeviceProperties": self.async_dispatch_device_properties,
"RenderingControl": self.async_update_volume,
"ZoneGroupTopology": self.async_update_groups,
}
dispatcher_send(self.hass, SONOS_CREATE_MEDIA_PLAYER, self)
dispatcher_send(self.hass, SONOS_SPEAKER_ADDED, self.soco.uid)
self.hass.create_task(self.async_subscribe())
self.hass.create_task(self.async_setup(entry, has_battery, dispatches))
#
# Entity management
@ -448,7 +442,7 @@ class SonosSpeaker:
# Save most recently processed event variables for cache and diagnostics
self._last_event_cache[event.service.service_type] = event.variables
dispatcher = self._event_dispatchers[event.service.service_type]
dispatcher(event)
dispatcher(self, event)
@callback
def async_dispatch_alarms(self, event: SonosEvent) -> None:
@ -1145,3 +1139,12 @@ class SonosSpeaker:
"""Update information about current volume settings."""
self.volume = self.soco.volume
self.muted = self.soco.mute
_event_dispatchers = {
"AlarmClock": async_dispatch_alarms,
"AVTransport": async_dispatch_media_update,
"ContentDirectory": async_dispatch_favorites,
"DeviceProperties": async_dispatch_device_properties,
"RenderingControl": async_update_volume,
"ZoneGroupTopology": async_update_groups,
}