Improve Sonos Spotify/Tidal support, add service exceptions (#51871)

This commit is contained in:
jjlawren 2021-06-17 04:09:57 -05:00 committed by GitHub
parent 016ba39dfb
commit d3724355cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 30 deletions

View file

@ -7,11 +7,13 @@ from typing import Any, Callable
from pysonos.exceptions import SoCoException, SoCoUPnPException
from homeassistant.exceptions import HomeAssistantError
_LOGGER = logging.getLogger(__name__)
def soco_error(errorcodes: list[str] | None = None) -> Callable:
"""Filter out specified UPnP errors from logs and avoid exceptions."""
"""Filter out specified UPnP errors and raise exceptions for service calls."""
def decorator(funct: Callable) -> Callable:
"""Decorate functions."""
@ -21,11 +23,15 @@ def soco_error(errorcodes: list[str] | None = None) -> Callable:
"""Wrap for all soco UPnP exception."""
try:
return funct(*args, **kwargs)
except SoCoUPnPException as err:
if not errorcodes or err.error_code not in errorcodes:
_LOGGER.error("Error on %s with %s", funct.__name__, err)
except SoCoException as err:
_LOGGER.error("Error on %s with %s", funct.__name__, err)
except (OSError, SoCoException, SoCoUPnPException) as err:
error_code = getattr(err, "error_code", None)
function = funct.__name__
if errorcodes and error_code in errorcodes:
_LOGGER.debug(
"Error code %s ignored in call to %s", error_code, function
)
return
raise HomeAssistantError(f"Error calling {function}: {err}") from err
return wrapper

View file

@ -13,8 +13,6 @@ from pysonos.core import (
PLAY_MODE_BY_MEANING,
PLAY_MODES,
)
from pysonos.exceptions import SoCoUPnPException
from pysonos.plugins.sharelink import ShareLinkPlugin
import voluptuous as vol
from homeassistant.components.media_player import MediaPlayerEntity
@ -518,6 +516,9 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
If media_id is a Plex payload, attempt Plex->Sonos playback.
If media_id is a Sonos or Tidal share link, attempt playback
using the respective service.
If media_type is "playlist", media_id should be a Sonos
Playlist name. Otherwise, media_id should be a URI.
@ -527,26 +528,19 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
if media_id and media_id.startswith(PLEX_URI_SCHEME):
media_id = media_id[len(PLEX_URI_SCHEME) :]
play_on_sonos(self.hass, media_type, media_id, self.name) # type: ignore[no-untyped-call]
elif media_type in (MEDIA_TYPE_MUSIC, MEDIA_TYPE_TRACK):
share_link = ShareLinkPlugin(soco)
if kwargs.get(ATTR_MEDIA_ENQUEUE):
try:
return
share_link = self.speaker.share_link
if share_link.is_share_link(media_id):
if kwargs.get(ATTR_MEDIA_ENQUEUE):
share_link.add_share_link_to_queue(media_id)
else:
soco.add_uri_to_queue(media_id)
except SoCoUPnPException:
_LOGGER.error(
'Error parsing media uri "%s", '
"please check it's a valid media resource "
"supported by Sonos",
media_id,
)
else:
if share_link.is_share_link(media_id):
soco.clear_queue()
share_link.add_share_link_to_queue(media_id)
soco.play_from_queue(0)
elif media_type in (MEDIA_TYPE_MUSIC, MEDIA_TYPE_TRACK):
if kwargs.get(ATTR_MEDIA_ENQUEUE):
soco.add_uri_to_queue(media_id)
else:
soco.play_uri(media_id)
elif media_type == MEDIA_TYPE_PLAYLIST:
@ -557,11 +551,12 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
try:
playlists = soco.get_sonos_playlists()
playlist = next(p for p in playlists if p.title == media_id)
except StopIteration:
_LOGGER.error('Could not find a Sonos playlist named "%s"', media_id)
else:
soco.clear_queue()
soco.add_to_queue(playlist)
soco.play_from_queue(0)
except StopIteration:
_LOGGER.error('Could not find a Sonos playlist named "%s"', media_id)
elif media_type in PLAYABLE_MEDIA_TYPES:
item = get_media(self.media.library, media_id, media_type) # type: ignore[no-untyped-call]

View file

@ -16,6 +16,7 @@ from pysonos.data_structures import DidlAudioBroadcast, DidlPlaylistContainer
from pysonos.events_base import Event as SonosEvent, SubscriptionBase
from pysonos.exceptions import SoCoException
from pysonos.music_library import MusicLibrary
from pysonos.plugins.sharelink import ShareLinkPlugin
from pysonos.snapshot import Snapshot
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
@ -147,6 +148,7 @@ class SonosSpeaker:
self.soco = soco
self.household_id: str = soco.household_id
self.media = SonosMedia(soco)
self._share_link_plugin: ShareLinkPlugin | None = None
# Synchronization helpers
self._is_ready: bool = False
@ -292,6 +294,13 @@ class SonosSpeaker:
"""Return true if player is a coordinator."""
return self.coordinator is None
@property
def share_link(self) -> ShareLinkPlugin:
"""Cache the ShareLinkPlugin instance for this speaker."""
if not self._share_link_plugin:
self._share_link_plugin = ShareLinkPlugin(self.soco)
return self._share_link_plugin
@property
def subscription_address(self) -> str | None:
"""Return the current subscription callback address if any."""
@ -476,6 +485,8 @@ class SonosSpeaker:
self, now: datetime.datetime | None = None, will_reconnect: bool = False
) -> None:
"""Make this player unavailable when it was not seen recently."""
self._share_link_plugin = None
if self._seen_timer:
self._seen_timer()
self._seen_timer = None