Improve Sonos Spotify/Tidal support, add service exceptions (#51871)
This commit is contained in:
parent
016ba39dfb
commit
d3724355cf
3 changed files with 42 additions and 30 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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,28 +528,21 @@ 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)
|
||||
return
|
||||
|
||||
share_link = self.speaker.share_link
|
||||
if share_link.is_share_link(media_id):
|
||||
if kwargs.get(ATTR_MEDIA_ENQUEUE):
|
||||
try:
|
||||
if share_link.is_share_link(media_id):
|
||||
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,
|
||||
)
|
||||
share_link.add_share_link_to_queue(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)
|
||||
else:
|
||||
soco.play_uri(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:
|
||||
if media_id.startswith("S:"):
|
||||
item = get_media(self.media.library, media_id, media_type) # type: ignore[no-untyped-call]
|
||||
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue