Detect battery-operated Sonos devices going offline (#65382)

pull/65442/head
jjlawren 2022-02-01 22:11:21 -06:00 committed by Franck Nijhof
parent 95d4be375c
commit 40a174cc70
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
2 changed files with 34 additions and 1 deletions

View File

@ -160,6 +160,7 @@ SONOS_SPEAKER_ACTIVITY = "sonos_speaker_activity"
SONOS_SPEAKER_ADDED = "sonos_speaker_added"
SONOS_STATE_UPDATED = "sonos_state_updated"
SONOS_REBOOTED = "sonos_rebooted"
SONOS_VANISHED = "sonos_vanished"
SOURCE_LINEIN = "Line-in"
SOURCE_TV = "TV"

View File

@ -12,6 +12,7 @@ from typing import Any
import urllib.parse
import async_timeout
import defusedxml.ElementTree as ET
from soco.core import MUSIC_SRC_LINE_IN, MUSIC_SRC_RADIO, MUSIC_SRC_TV, SoCo
from soco.data_structures import DidlAudioBroadcast, DidlPlaylistContainer
from soco.events_base import Event as SonosEvent, SubscriptionBase
@ -56,6 +57,7 @@ from .const import (
SONOS_STATE_PLAYING,
SONOS_STATE_TRANSITIONING,
SONOS_STATE_UPDATED,
SONOS_VANISHED,
SOURCE_LINEIN,
SOURCE_TV,
SUBSCRIPTION_TIMEOUT,
@ -225,6 +227,7 @@ class SonosSpeaker:
(SONOS_SPEAKER_ADDED, self.update_group_for_uid),
(f"{SONOS_REBOOTED}-{self.soco.uid}", self.async_rebooted),
(f"{SONOS_SPEAKER_ACTIVITY}-{self.soco.uid}", self.speaker_activity),
(f"{SONOS_VANISHED}-{self.soco.uid}", self.async_vanished),
)
for (signal, target) in dispatch_pairs:
@ -388,6 +391,8 @@ class SonosSpeaker:
async def async_unsubscribe(self) -> None:
"""Cancel all subscriptions."""
if not self._subscriptions:
return
_LOGGER.debug("Unsubscribing from events for %s", self.zone_name)
results = await asyncio.gather(
*(subscription.unsubscribe() for subscription in self._subscriptions),
@ -572,6 +577,15 @@ class SonosSpeaker:
self.hass.data[DATA_SONOS].discovery_known.discard(self.soco.uid)
self.async_write_entity_states()
async def async_vanished(self, reason: str) -> None:
"""Handle removal of speaker when marked as vanished."""
if not self.available:
return
_LOGGER.debug(
"%s has vanished (%s), marking unavailable", self.zone_name, reason
)
await self.async_offline()
async def async_rebooted(self, soco: SoCo) -> None:
"""Handle a detected speaker reboot."""
_LOGGER.warning(
@ -685,7 +699,25 @@ class SonosSpeaker:
@callback
def async_update_groups(self, event: SonosEvent) -> None:
"""Handle callback for topology change event."""
if not hasattr(event, "zone_player_uui_ds_in_group"):
if xml := event.variables.get("zone_group_state"):
zgs = ET.fromstring(xml)
for vanished_device in zgs.find("VanishedDevices"):
if (reason := vanished_device.get("Reason")) != "sleeping":
_LOGGER.debug(
"Ignoring %s marked %s as vanished with reason: %s",
self.zone_name,
vanished_device.get("ZoneName"),
reason,
)
continue
uid = vanished_device.get("UUID")
async_dispatcher_send(
self.hass,
f"{SONOS_VANISHED}-{uid}",
reason,
)
if "zone_player_uui_ds_in_group" not in event.variables:
return
self.event_stats.process(event)
self.hass.async_create_task(self.create_update_groups_coro(event))