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.data_structures import DidlAudioBroadcast, DidlPlaylistContainer
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.plugins.sharelink import ShareLinkPlugin
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.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as ent_reg
from homeassistant.helpers.dispatcher import (
async_dispatcher_send,
@ -802,27 +803,58 @@ class SonosSpeaker:
"""Restore snapshots for all the speakers."""
def _restore_groups(
speakers: list[SonosSpeaker], with_group: bool
speakers: set[SonosSpeaker], with_group: bool
) -> list[list[SonosSpeaker]]:
"""Pause all current coordinators and restore groups."""
for speaker in (s for s in speakers if s.is_coordinator):
if speaker.media.playback_status == SONOS_STATE_PLAYING:
speaker.soco.pause()
if (
speaker.media.playback_status == SONOS_STATE_PLAYING
and "Pause" in speaker.soco.available_actions
):
try:
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 = []
if not with_group:
return groups
if with_group:
# Unjoin slaves first to prevent inheritance of queues
for speaker in [s for s in speakers if not s.is_coordinator]:
if speaker.snapshot_group != speaker.sonos_group:
speaker.unjoin()
# Unjoin non-coordinator speakers not contained in the desired snapshot group
#
# If a coordinator is unjoined from its group, another speaker from the 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
# Bring back the original group topology
for speaker in (s for s in speakers if s.snapshot_group):
assert speaker.snapshot_group is not None
if speaker.snapshot_group[0] == speaker:
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()
# Bring back the original group topology
for speaker in (s for s in speakers if s.snapshot_group):
assert speaker.snapshot_group is not None
if speaker.snapshot_group[0] == speaker:
if (
speaker.snapshot_group != speaker.sonos_group
and speaker.snapshot_group != [speaker]
):
speaker.join(speaker.snapshot_group)
groups.append(speaker.snapshot_group.copy())
groups.append(speaker.snapshot_group.copy())
return groups
@ -836,6 +868,11 @@ class SonosSpeaker:
# Find all affected players
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:
for speaker in [s for s in speakers_set if s.snapshot_group]:
assert speaker.snapshot_group is not None