Handle failures during initial Sonos subscription (#73456)
parent
143e6a7adc
commit
86fde1a644
|
@ -7,6 +7,10 @@ class UnknownMediaType(BrowseError):
|
|||
"""Unknown media type."""
|
||||
|
||||
|
||||
class SonosSubscriptionsFailed(HomeAssistantError):
|
||||
"""Subscription creation failed."""
|
||||
|
||||
|
||||
class SonosUpdateError(HomeAssistantError):
|
||||
"""Update failed."""
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ from .const import (
|
|||
SONOS_VANISHED,
|
||||
SUBSCRIPTION_TIMEOUT,
|
||||
)
|
||||
from .exception import S1BatteryMissing, SonosUpdateError
|
||||
from .exception import S1BatteryMissing, SonosSubscriptionsFailed, SonosUpdateError
|
||||
from .favorites import SonosFavorites
|
||||
from .helpers import soco_error
|
||||
from .media import SonosMedia
|
||||
|
@ -324,12 +324,29 @@ class SonosSpeaker:
|
|||
async with self._subscription_lock:
|
||||
if self._subscriptions:
|
||||
return
|
||||
await self._async_subscribe()
|
||||
try:
|
||||
await self._async_subscribe()
|
||||
except SonosSubscriptionsFailed:
|
||||
_LOGGER.warning("Creating subscriptions failed for %s", self.zone_name)
|
||||
await self._async_offline()
|
||||
|
||||
async def _async_subscribe(self) -> None:
|
||||
"""Create event subscriptions."""
|
||||
_LOGGER.debug("Creating subscriptions for %s", self.zone_name)
|
||||
|
||||
subscriptions = [
|
||||
self._subscribe(getattr(self.soco, service), self.async_dispatch_event)
|
||||
for service in SUBSCRIPTION_SERVICES
|
||||
]
|
||||
results = await asyncio.gather(*subscriptions, return_exceptions=True)
|
||||
for result in results:
|
||||
self.log_subscription_result(
|
||||
result, "Creating subscription", logging.WARNING
|
||||
)
|
||||
|
||||
if any(isinstance(result, Exception) for result in results):
|
||||
raise SonosSubscriptionsFailed
|
||||
|
||||
# Create a polling task in case subscriptions fail or callback events do not arrive
|
||||
if not self._poll_timer:
|
||||
self._poll_timer = async_track_time_interval(
|
||||
|
@ -342,16 +359,6 @@ class SonosSpeaker:
|
|||
SCAN_INTERVAL,
|
||||
)
|
||||
|
||||
subscriptions = [
|
||||
self._subscribe(getattr(self.soco, service), self.async_dispatch_event)
|
||||
for service in SUBSCRIPTION_SERVICES
|
||||
]
|
||||
results = await asyncio.gather(*subscriptions, return_exceptions=True)
|
||||
for result in results:
|
||||
self.log_subscription_result(
|
||||
result, "Creating subscription", logging.WARNING
|
||||
)
|
||||
|
||||
async def _subscribe(
|
||||
self, target: SubscriptionBase, sub_callback: Callable
|
||||
) -> None:
|
||||
|
@ -585,6 +592,11 @@ class SonosSpeaker:
|
|||
await self.async_offline()
|
||||
|
||||
async def async_offline(self) -> None:
|
||||
"""Handle removal of speaker when unavailable."""
|
||||
async with self._subscription_lock:
|
||||
await self._async_offline()
|
||||
|
||||
async def _async_offline(self) -> None:
|
||||
"""Handle removal of speaker when unavailable."""
|
||||
if not self.available:
|
||||
return
|
||||
|
@ -602,8 +614,7 @@ class SonosSpeaker:
|
|||
self._poll_timer()
|
||||
self._poll_timer = None
|
||||
|
||||
async with self._subscription_lock:
|
||||
await self.async_unsubscribe()
|
||||
await self.async_unsubscribe()
|
||||
|
||||
self.hass.data[DATA_SONOS].discovery_known.discard(self.soco.uid)
|
||||
|
||||
|
|
|
@ -29,3 +29,21 @@ async def test_fallback_to_polling(
|
|||
assert speaker.subscriptions_failed
|
||||
assert "falling back to polling" in caplog.text
|
||||
assert "Activity on Zone A from SonosSpeaker.update_volume" in caplog.text
|
||||
|
||||
|
||||
async def test_subscription_creation_fails(hass: HomeAssistant, async_setup_sonos):
|
||||
"""Test that subscription creation failures are handled."""
|
||||
with patch(
|
||||
"homeassistant.components.sonos.speaker.SonosSpeaker._subscribe",
|
||||
side_effect=ConnectionError("Took too long"),
|
||||
):
|
||||
await async_setup_sonos()
|
||||
|
||||
speaker = list(hass.data[DATA_SONOS].discovered.values())[0]
|
||||
assert not speaker._subscriptions
|
||||
|
||||
with patch.object(speaker, "_resub_cooldown_expires_at", None):
|
||||
speaker.speaker_activity("discovery")
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert speaker._subscriptions
|
||||
|
|
Loading…
Reference in New Issue