Handle more Sonos snapshot restore scenarios (#53277)

This commit is contained in:
jjlawren 2021-07-22 13:04:02 -05:00 committed by GitHub
parent b2528e97b6
commit 0707792bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,7 +14,7 @@ import async_timeout
from pysonos.core import MUSIC_SRC_LINE_IN, MUSIC_SRC_RADIO, MUSIC_SRC_TV, SoCo from pysonos.core import MUSIC_SRC_LINE_IN, MUSIC_SRC_RADIO, MUSIC_SRC_TV, SoCo
from pysonos.data_structures import DidlAudioBroadcast, DidlPlaylistContainer from pysonos.data_structures import DidlAudioBroadcast, DidlPlaylistContainer
from pysonos.events_base import Event as SonosEvent, SubscriptionBase from pysonos.events_base import Event as SonosEvent, SubscriptionBase
from pysonos.exceptions import SoCoException from pysonos.exceptions import SoCoException, SoCoUPnPException
from pysonos.music_library import MusicLibrary from pysonos.music_library import MusicLibrary
from pysonos.plugins.sharelink import ShareLinkPlugin from pysonos.plugins.sharelink import ShareLinkPlugin
from pysonos.snapshot import Snapshot from pysonos.snapshot import Snapshot
@ -25,6 +25,7 @@ from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as ent_reg from homeassistant.helpers import entity_registry as ent_reg
from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.dispatcher import (
async_dispatcher_send, async_dispatcher_send,
@ -802,25 +803,56 @@ class SonosSpeaker:
"""Restore snapshots for all the speakers.""" """Restore snapshots for all the speakers."""
def _restore_groups( def _restore_groups(
speakers: list[SonosSpeaker], with_group: bool speakers: set[SonosSpeaker], with_group: bool
) -> list[list[SonosSpeaker]]: ) -> list[list[SonosSpeaker]]:
"""Pause all current coordinators and restore groups.""" """Pause all current coordinators and restore groups."""
for speaker in (s for s in speakers if s.is_coordinator): for speaker in (s for s in speakers if s.is_coordinator):
if speaker.media.playback_status == SONOS_STATE_PLAYING: if (
speaker.media.playback_status == SONOS_STATE_PLAYING
and "Pause" in speaker.soco.available_actions
):
try:
speaker.soco.pause() speaker.soco.pause()
except SoCoUPnPException as exc:
_LOGGER.debug(
"Pause failed during restore of %s: %s",
speaker.zone_name,
speaker.soco.available_actions,
exc_info=exc,
)
groups = [] groups = []
if not with_group:
return groups
if with_group: # Unjoin non-coordinator speakers not contained in the desired snapshot group
# Unjoin slaves first to prevent inheritance of queues #
for speaker in [s for s in speakers if not s.is_coordinator]: # If a coordinator is unjoined from its group, another speaker from the group
if speaker.snapshot_group != speaker.sonos_group: # will inherit the coordinator's playqueue and its own playqueue will be lost
speakers_to_unjoin = set()
for speaker in speakers:
if speaker.sonos_group == speaker.snapshot_group:
continue
speakers_to_unjoin.update(
{
s
for s in speaker.sonos_group[1:]
if s not in speaker.snapshot_group
}
)
for speaker in speakers_to_unjoin:
speaker.unjoin() speaker.unjoin()
# Bring back the original group topology # Bring back the original group topology
for speaker in (s for s in speakers if s.snapshot_group): for speaker in (s for s in speakers if s.snapshot_group):
assert speaker.snapshot_group is not None assert speaker.snapshot_group is not None
if speaker.snapshot_group[0] == speaker: if speaker.snapshot_group[0] == speaker:
if (
speaker.snapshot_group != speaker.sonos_group
and speaker.snapshot_group != [speaker]
):
speaker.join(speaker.snapshot_group) speaker.join(speaker.snapshot_group)
groups.append(speaker.snapshot_group.copy()) groups.append(speaker.snapshot_group.copy())
@ -836,6 +868,11 @@ class SonosSpeaker:
# Find all affected players # Find all affected players
speakers_set = {s for s in speakers if s.soco_snapshot} speakers_set = {s for s in speakers if s.soco_snapshot}
if missing_snapshots := set(speakers) - speakers_set:
raise HomeAssistantError(
f"Restore failed, speakers are missing snapshots: {[s.zone_name for s in missing_snapshots]}"
)
if with_group: if with_group:
for speaker in [s for s in speakers_set if s.snapshot_group]: for speaker in [s for s in speakers_set if s.snapshot_group]:
assert speaker.snapshot_group is not None assert speaker.snapshot_group is not None