From c362ffd384fba2820b8eb3c1f3ed7d13a4ee47cf Mon Sep 17 00:00:00 2001 From: jjlawren Date: Wed, 9 Jun 2021 23:31:14 -0500 Subject: [PATCH] Clean up unused Sonos subscriptions (#51583) --- homeassistant/components/sonos/__init__.py | 4 ++++ homeassistant/components/sonos/entity.py | 5 +++-- homeassistant/components/sonos/speaker.py | 18 ++++++++++++------ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 4bb9b475b9a..a20805ce136 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -129,6 +129,10 @@ async def async_setup_entry( # noqa: C901 pysonos.config.EVENT_ADVERTISE_IP = advertise_addr async def _async_stop_event_listener(event: Event) -> None: + await asyncio.gather( + *[speaker.async_unsubscribe() for speaker in data.discovered.values()], + return_exceptions=True, + ) if events_asyncio.event_listener: await events_asyncio.event_listener.async_stop() diff --git a/homeassistant/components/sonos/entity.py b/homeassistant/components/sonos/entity.py index 7d4e168c960..290c6a64cb1 100644 --- a/homeassistant/components/sonos/entity.py +++ b/homeassistant/components/sonos/entity.py @@ -64,13 +64,14 @@ class SonosEntity(Entity): async def async_poll(self, now: datetime.datetime) -> None: """Poll the entity if subscriptions fail.""" - if self.speaker.is_first_poll: + if not self.speaker.subscriptions_failed: _LOGGER.warning( "%s cannot reach [%s], falling back to polling, functionality may be limited", self.speaker.zone_name, self.speaker.subscription_address, ) - self.speaker.is_first_poll = False + self.speaker.subscriptions_failed = True + await self.speaker.async_unsubscribe() try: await self.async_update() # pylint: disable=no-member except (OSError, SoCoException) as ex: diff --git a/homeassistant/components/sonos/speaker.py b/homeassistant/components/sonos/speaker.py index 47c9af62761..2ddd7148478 100644 --- a/homeassistant/components/sonos/speaker.py +++ b/homeassistant/components/sonos/speaker.py @@ -149,11 +149,11 @@ class SonosSpeaker: 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_failed: bool = False self._subscriptions: list[SubscriptionBase] = [] self._resubscription_lock: asyncio.Lock | None = None self._event_dispatchers: dict[str, Callable] = {} @@ -331,6 +331,15 @@ class SonosSpeaker: subscription.auto_renew_fail = self.async_renew_failed self._subscriptions.append(subscription) + async def async_unsubscribe(self) -> None: + """Cancel all subscriptions.""" + _LOGGER.debug("Unsubscribing from events for %s", self.zone_name) + await asyncio.gather( + *[subscription.unsubscribe() for subscription in self._subscriptions], + return_exceptions=True, + ) + self._subscriptions = [] + @callback def async_renew_failed(self, exception: Exception) -> None: """Handle a failed subscription renewal.""" @@ -445,7 +454,7 @@ class SonosSpeaker: SCAN_INTERVAL, ) - if self._is_ready: + if self._is_ready and not self.subscriptions_failed: done = await self.async_subscribe() if not done: assert self._seen_timer is not None @@ -466,10 +475,7 @@ class SonosSpeaker: self._poll_timer() self._poll_timer = None - for subscription in self._subscriptions: - await subscription.unsubscribe() - - self._subscriptions = [] + await self.async_unsubscribe() if not will_reconnect: self.hass.data[DATA_SONOS].ssdp_known.remove(self.soco.uid)