Files
HomeAssistance/custom_components/spotifyplus/media_player.py
T
2026-06-05 22:34:31 -04:00

9583 lines
440 KiB
Python

"""Support for interacting with Spotify Connect."""
# Important notes about HA State writes:
#
# `self.async_write_ha_state()` should always be used inside of the event loop (any method that is async itself or a callback).
# If you are in a `async def` method or one wrapped in `@callback`, use `async_write_ha_state` since you are inside of the event loop.
# `self.schedule_update_ha_state(force_refresh=False)` should be unsed when not inside of the event loop (e.g. for sync functions that are ran
# inside of the executor thread). If you are in a `def` method (no async) then use `schedule_update_ha_state` since you are inside of the event loop.
from __future__ import annotations
import datetime as dt
from datetime import timedelta, datetime
from pprint import pformat
from typing import Any, Callable, Concatenate, ParamSpec, TypeVar, Tuple
from yarl import URL
from spotifywebapipython import SpotifyClient, SpotifyDiscovery, SpotifyApiError, SpotifyMediaTypes, SpotifyWebApiError
from spotifywebapipython.zeroconfapi import *
from spotifywebapipython.models import (
Album,
AlbumPageSaved,
AlbumPageSimplified,
Artist,
ArtistInfo,
ArtistPage,
Audiobook,
AudiobookPageSimplified,
AudioFeatures,
Category,
CategoryPage,
Chapter,
Context,
Device as PlayerDevice,
Episode,
EpisodePageSaved,
EpisodePageSimplified,
ImagePaletteColors,
ImageVibrantColors,
PlayerLastPlayedInfo,
PlayerPlayState,
PlayerQueueInfo,
PlayHistoryPage,
Playlist,
PlaylistPage,
PlaylistPageSimplified,
SearchResponse,
Show,
ShowPageSaved,
SpotifyConnectDevice,
SpotifyConnectDevices,
Track,
TrackRecommendations,
TrackPage,
TrackPageSaved,
TrackPageSimplified,
UserProfile
)
from spotifywebapipython.sautils import (
validateDelay
)
from homeassistant.components.media_player import (
ATTR_MEDIA_ENQUEUE,
BrowseMedia,
MediaPlayerEnqueue,
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaType,
RepeatMode,
SearchMedia,
SearchMediaQuery,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError, IntegrationError, ServiceValidationError
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity_registry import EntityRegistry, RegistryEntry
from homeassistant.util.dt import utcnow
from .appmessages import STAppMessages
from .browse_media import (
async_browse_media_library_index,
BrowsableMedia,
browse_media_node,
PLAYABLE_MEDIA_TYPES,
SPOTIFY_LIBRARY_MAP
)
from .search_media import (
search_media_node,
)
from .instancedata_spotifyplus import InstanceDataSpotifyPlus
from .const import (
ATTR_SPOTIFYPLUS_ARTIST_URI,
ATTR_SPOTIFYPLUS_CONTEXT_URI,
ATTR_SPOTIFYPLUS_DEVICE_ID,
ATTR_SPOTIFYPLUS_DEVICE_NAME,
ATTR_SPOTIFYPLUS_DEVICE_IS_BRAND_SONOS,
ATTR_SPOTIFYPLUS_DEVICE_IS_CHROMECAST,
ATTR_SPOTIFYPLUS_DEVICE_IS_RESTRICTED,
ATTR_SPOTIFYPLUS_DEVICE_MUSIC_SOURCE,
ATTR_SPOTIFYPLUS_ITEM_TYPE,
ATTR_SPOTIFYPLUS_NOWPLAYING_IMAGE_URL,
ATTR_SPOTIFYPLUS_PLAY_TIME_REMAINING_EST,
ATTR_SPOTIFYPLUS_PLAYING_TYPE,
ATTR_SPOTIFYPLUS_PLAYLIST_NAME,
ATTR_SPOTIFYPLUS_PLAYLIST_URI,
ATTR_SPOTIFYPLUS_SOURCE_LIST_HIDE,
ATTR_SPOTIFYPLUS_TRACK_IS_EXPLICIT,
ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN,
ATTR_SPOTIFYPLUS_USER_COUNTRY,
ATTR_SPOTIFYPLUS_USER_DISPLAY_NAME,
ATTR_SPOTIFYPLUS_USER_EMAIL,
ATTR_SPOTIFYPLUS_USER_HAS_WEB_PLAYER_CREDENTIALS,
ATTR_SPOTIFYPLUS_USER_ID,
ATTR_SPOTIFYPLUS_USER_PRODUCT,
ATTR_SPOTIFYPLUS_USER_URI,
ATTR_VOLUME_STEP,
DEFAULT_OPTION_SPOTIFY_SCAN_INTERVAL,
DOMAIN,
DOMAIN_SCRIPT,
LOGGER,
)
from .utils import (
passwordMaskString,
)
# get smartinspect logger reference; create a new session for this module name.
from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext, SIColors
_logsi:SISession = SIAuto.Si.GetSession(__name__)
if (_logsi == None):
_logsi = SIAuto.Si.AddSession(__name__, True)
_logsi.SystemLogger = LOGGER
# Sonos constants.
SONOS_NOT_IMPLEMENTED:str = 'NOT_IMPLEMENTED'
SONOS_COMMAND_DELAY:float = 0.50
REPEAT_TO_SONOS = {
RepeatMode.OFF: False,
RepeatMode.ALL: True,
RepeatMode.ONE: "ONE",
}
SONOS_TO_REPEAT = {meaning: mode for mode, meaning in REPEAT_TO_SONOS.items()}
# our extra state attribute defaults.
ATTRVALUE_NO_DEVICE = "no_device"
ATTRVALUE_UNKNOWN = "unknown"
ATTRVALUE_NOT_SET = "notset"
# annotate the `spotify_exception_handler` callable.
_SpotifyMediaPlayerT = TypeVar("_SpotifyMediaPlayerT", bound="SpotifyMediaPlayer")
_R = TypeVar("_R")
_P = ParamSpec("_P")
SCAN_INTERVAL = timedelta(seconds=1)
"""
Time interval (in seconds) for HA to scan for status updates.
Note that the Spotify Web API does not get called every time HA update method is called
though; check update method for logic that controls this.
"""
SPOTIFY_SCAN_INTERVAL_TRACK_ENDSTART:int = 3
"""
Time interval (in seconds) to scan spotify connect player for updates
due to a track ending / starting.
"""
SPOTIFY_SCAN_INTERVAL_COMMAND:int = 5
"""
Time interval (in seconds) to scan spotify connect player for updates
due to a player command. This gives the Spotify Connect Player time to
update its PlayState status (5 seconds).
"""
REPEAT_MODE_MAPPING_TO_HA = {
"context": RepeatMode.ALL,
"off": RepeatMode.OFF,
"track": RepeatMode.ONE,
}
""" maps spotify repeat modes to home assistant repeat modes. """
REPEAT_MODE_MAPPING_TO_SPOTIFY = {
value: key for key, value in REPEAT_MODE_MAPPING_TO_HA.items()
}
""" maps home assistant repeat modes to spotify repeat modes. """
async def async_setup_entry(hass:HomeAssistant, entry:ConfigEntry, async_add_entities:AddEntitiesCallback) -> None:
"""
Set up the media player based on a config entry.
Args:
hass (HomeAssistant):
HomeAssistant instance.
entry (ConfigEntry):
HomeAssistant configuration entry dictionary. This contains configuration
settings for the specific component device entry.
async_add_entities (AddEntitiesCallback):
Callback function to add all entities to Home Assistant for this platform.
This function is called as part of the __init__.async_setup_entry event flow,
which was initiated via the `hass.config_entries.async_forward_entry_setup` call.
"""
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogObject(SILevel.Verbose, "'%s': MediaPlayer async_setup_entry is starting - entry (ConfigEntry) object" % entry.title, entry)
# get integration instance data from HA datastore.
data:InstanceDataSpotifyPlus = hass.data[DOMAIN][entry.entry_id]
# create the platform instance, passing our initialization parameters.
_logsi.LogVerbose("'%s': MediaPlayer async_setup_entry is creating the SpotifyMediaPlayer instance" % entry.title)
media_player = SpotifyMediaPlayer(data)
# add all entities to Home Assistant.
_logsi.LogVerbose("'%s': MediaPlayer async_setup_entry is adding SpotifyMediaPlayer instance entities to Home Assistant" % entry.title)
async_add_entities([media_player], True)
# store the reference to the media player object.
_logsi.LogVerbose("'%s': MediaPlayer async_setup_entry is storing the SpotifyMediaPlayer reference to hass.data[DOMAIN]" % entry.title)
hass.data[DOMAIN][entry.entry_id].media_player = media_player
# trace.
_logsi.LogVerbose("'%s': MediaPlayer async_setup_entry complete" % entry.title)
except Exception as ex:
# trace.
_logsi.LogException("'%s': MediaPlayer async_setup_entry exception" % entry.title, ex, logToSystemLogger=False)
raise
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
def spotify_exception_handler(
func: Callable[Concatenate[_SpotifyMediaPlayerT, _P], _R]
) -> Callable[Concatenate[_SpotifyMediaPlayerT, _P], _R | None]:
"""
Decorate SpotifyClient calls to handle Spotify exception.
A decorator that wraps the passed in function, catches Spotify errors,
aiohttp exceptions and handles the availability of the media player.
"""
def wrapper(self: _SpotifyMediaPlayerT, *args: _P.args, **kwargs: _P.kwargs) -> _R | None:
try:
# indicate we are in a command event.
self._isInCommandEvent = True
_logsi.WatchDateTime(SILevel.Debug, "HASpotifyCommandEventLastDT", datetime.now())
# call the function.
result = func(self, *args, **kwargs)
# do not update HA state in this handler! doing so causes UI buttons
# pressed to "toggle" between states. the "self.schedule_update_ha_state(force_refresh=False)"
# call should be done in the individual methods.
# return function result to caller.
return result
except ServiceValidationError: raise # pass handled exceptions on thru
except HomeAssistantError: raise # pass handled exceptions on thru
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex
finally:
# indicate we are NOT in a command event.
self._isInCommandEvent = False
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
return wrapper
class SpotifyMediaPlayer(MediaPlayerEntity):
"""
Representation of a SpotifyPlus media player device.
"""
def __init__(self, data:InstanceDataSpotifyPlus) -> None:
"""
Initializes a new instance of the SpotifyPlus media player entity class.
Args:
data (InstanceDataSpotifyPlus):
The media player entity instance data parameters that were created
in the `__init__.async_setup_entry` method.
"""
methodParms:SIMethodParmListContext = None
try:
# trace.
methodParms = _logsi.EnterMethodParmList(SILevel.Debug)
methodParms.AppendKeyValue("data.media_player", str(data.media_player))
methodParms.AppendKeyValue("data.session", str(data.session))
methodParms.AppendKeyValue("data.spotifyClient", str(data.spotifyClient))
_logsi.LogMethodParmList(SILevel.Verbose, "SpotifyMediaPlayer initialization arguments", methodParms)
# initialize instance storage.
self._id = data.spotifyClient.UserProfile.Id
self._spotifyScanInterval = DEFAULT_OPTION_SPOTIFY_SCAN_INTERVAL
self._playlist:Playlist = None
self._lastMediaPlayedPosition:int = 0
self.data = data
self._currentScanInterval:int = 0
self._commandScanInterval:int = 0
self._lastKnownTimeRemainingSeconds:int = 0
self._playTimeRemainingEst:int = 0
self._isInCommandEvent:bool = False
self._isInUpdateEvent:bool = False
self._source_at_poweroff:str = None
self._source_at_poweron:str = None
self._volume_level_saved:float = None
self._playerState:PlayerPlayState = PlayerPlayState()
self._spotifyConnectDevice:SpotifyConnectDevice = None
# initialize base class attributes (MediaPlayerEntity).
self._attr_icon = "mdi:spotify"
self._attr_media_image_remotely_accessible = False
self._attr_state = MediaPlayerState.OFF
self._attr_volume_step:float = 0.1
# A unique_id for this entity within this domain.
# Note: This is NOT used to generate the user visible Entity ID used in automations.
# this value should match the value assigned in config_flow `async_set_unique_id` method call.
self._attr_unique_id = data.spotifyClient.UserProfile.Id + "_" + DOMAIN
# we will set "self._attr_has_entity_name = False", which causes the "self._attr_name"
# to be used as-is. use "self._attr_has_entity_name = True", to append the "self._attr_name"
# value to the end of "DeviceInfo.name" value.
self._attr_has_entity_name = False
self._attr_name = f"SpotifyPlus {data.spotifyClient.UserProfile.DisplayName}"
# set device information.
# this contains information about the device that is partially visible in the UI.
# for more information see: https://developers.home-assistant.io/docs/device_registry_index/#device-properties
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, data.spotifyClient.UserProfile.Id)},
manufacturer="Spotify AB",
model=f"Spotify {self.data.spotifyClient.UserProfile.Product.capitalize()} Account",
name=self._attr_name,
entry_type=DeviceEntryType.SERVICE,
configuration_url="https://open.spotify.com",
)
_logsi.LogDictionary(SILevel.Verbose, "'%s': MediaPlayer device information dictionary" % self.name, self._attr_device_info, prettyPrint=True)
# set features supported by this media player.
# most features will NOT be supported if the Spotify user does not have a PREMIUM
# membership level, as the Spotify Web API requires a premium membership for most
# of the player control functions.
if (self.data.spotifyClient.UserProfile.IsProductPremium):
_logsi.LogVerbose("'%s': MediaPlayer is setting supported features for Spotify Premium user" % self.name)
self._attr_supported_features = MediaPlayerEntityFeature.BROWSE_MEDIA \
| MediaPlayerEntityFeature.MEDIA_ENQUEUE \
| MediaPlayerEntityFeature.NEXT_TRACK \
| MediaPlayerEntityFeature.PAUSE \
| MediaPlayerEntityFeature.PLAY \
| MediaPlayerEntityFeature.PLAY_MEDIA \
| MediaPlayerEntityFeature.PREVIOUS_TRACK \
| MediaPlayerEntityFeature.REPEAT_SET \
| MediaPlayerEntityFeature.SEARCH_MEDIA \
| MediaPlayerEntityFeature.SEEK \
| MediaPlayerEntityFeature.SELECT_SOURCE \
| MediaPlayerEntityFeature.SHUFFLE_SET \
| MediaPlayerEntityFeature.TURN_OFF \
| MediaPlayerEntityFeature.TURN_ON \
| MediaPlayerEntityFeature.VOLUME_MUTE \
| MediaPlayerEntityFeature.VOLUME_SET \
| MediaPlayerEntityFeature.VOLUME_STEP
elif (self.data.spotifyClient.HasSpotifyWebPlayerCredentials):
_logsi.LogVerbose("'%s': MediaPlayer is setting supported features for Spotify Non-Premium user (SpotifyWebPlayerCredentials configured)" % self.name)
# certain features are disabled for non-premium accounts when using SpotifyWebPlayerCredentials.
# | MediaPlayerEntityFeature.PREVIOUS_TRACK \
# | MediaPlayerEntityFeature.SEEK \
# | MediaPlayerEntityFeature.SHUFFLE_SET \
self._attr_supported_features = MediaPlayerEntityFeature.BROWSE_MEDIA \
| MediaPlayerEntityFeature.MEDIA_ENQUEUE \
| MediaPlayerEntityFeature.NEXT_TRACK \
| MediaPlayerEntityFeature.PAUSE \
| MediaPlayerEntityFeature.PLAY \
| MediaPlayerEntityFeature.PLAY_MEDIA \
| MediaPlayerEntityFeature.REPEAT_SET \
| MediaPlayerEntityFeature.SEARCH_MEDIA \
| MediaPlayerEntityFeature.SELECT_SOURCE \
| MediaPlayerEntityFeature.TURN_OFF \
| MediaPlayerEntityFeature.TURN_ON \
| MediaPlayerEntityFeature.VOLUME_MUTE \
| MediaPlayerEntityFeature.VOLUME_SET \
| MediaPlayerEntityFeature.VOLUME_STEP
else:
_logsi.LogVerbose("'%s': MediaPlayer is setting supported features for Spotify Non-Premium user" % self.name)
self._attr_supported_features = MediaPlayerEntityFeature.BROWSE_MEDIA \
| MediaPlayerEntityFeature.SEARCH_MEDIA \
| MediaPlayerEntityFeature.TURN_OFF \
| MediaPlayerEntityFeature.TURN_ON
# we will (by default) set polling to true, as the SpotifyClient does not support websockets
# for player update notifications.
_logsi.LogVerbose("'%s': MediaPlayer device polling is being enabled, as the device does not support websockets" % self.name)
self._attr_should_poll = True
# set scan interval based on configuration options.
self._spotifyScanInterval = data.OptionSpotifyScanInterval
# disable turn on / off features based on configuration options.
if data.OptionAlwaysOn:
_logsi.LogVerbose("'%s': MediaPlayer always on option enabled; disabling turn on / off features, and setting initial state to IDLE" % self.name)
self._attr_supported_features &= ~MediaPlayerEntityFeature.TURN_OFF
self._attr_supported_features &= ~MediaPlayerEntityFeature.TURN_ON
self._attr_state = MediaPlayerState.IDLE
# set default device id to use for player transport functions that are executed
# when there is no active Spotify player device.
value:str = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)
if (value is None):
value = PlayerDevice.GetNameFromSelectItem(self.data.OptionDeviceDefault)
self.data.spotifyClient.DefaultDeviceId = value
# trace.
_logsi.LogObject(SILevel.Verbose, "'%s': MediaPlayer SpotifyClient object" % self.name, self.data.spotifyClient)
_logsi.LogObject(SILevel.Verbose, "'%s': MediaPlayer initialization complete" % self.name, self)
except Exception as ex:
# trace.
_logsi.LogException("'%s': MediaPlayer initialization exception" % self.name, ex, logToSystemLogger=False)
raise
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
@property
def extra_state_attributes(self) -> dict:
""" Return entity specific state attributes. """
# build list of our extra state attributes to return to HA UI.
attributes = {}
attributes[ATTR_SPOTIFYPLUS_DEVICE_ID] = ATTRVALUE_NO_DEVICE
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_BRAND_SONOS] = False
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_CHROMECAST] = False
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_RESTRICTED] = False
attributes[ATTR_SPOTIFYPLUS_DEVICE_MUSIC_SOURCE] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_DEVICE_NAME] = ATTRVALUE_NO_DEVICE
attributes[ATTR_SPOTIFYPLUS_ITEM_TYPE] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_NOWPLAYING_IMAGE_URL] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_PLAY_TIME_REMAINING_EST] = None
attributes[ATTR_SPOTIFYPLUS_PLAYING_TYPE] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_TRACK_IS_EXPLICIT] = False
attributes[ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_COUNTRY] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_DISPLAY_NAME] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_EMAIL] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_HAS_WEB_PLAYER_CREDENTIALS] = False
attributes[ATTR_SPOTIFYPLUS_USER_ID] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_PRODUCT] = ATTRVALUE_UNKNOWN
attributes[ATTR_SPOTIFYPLUS_USER_URI] = ATTRVALUE_UNKNOWN
# add media player entity information.
attributes[ATTR_VOLUME_STEP] = self._attr_volume_step
# add configuration options information.
if (self.data is not None):
attributes[ATTR_SPOTIFYPLUS_SOURCE_LIST_HIDE] = self.data.OptionSourceListHide
# add currently active playstate information.
if self._playerState is not None:
if self._playerState.Device is not None:
attributes[ATTR_SPOTIFYPLUS_DEVICE_ID] = self._playerState.Device.Id
attributes[ATTR_SPOTIFYPLUS_DEVICE_NAME] = self._playerState.Device.Name
attributes[ATTR_SPOTIFYPLUS_DEVICE_MUSIC_SOURCE] = self._playerState.DeviceMusicSource
if self._playerState.Context is not None:
attributes[ATTR_SPOTIFYPLUS_CONTEXT_URI] = self._playerState.Context.Uri
attributes['media_context_content_id'] = self._playerState.Context.Uri
if self._playerState.ItemType is not None:
attributes[ATTR_SPOTIFYPLUS_ITEM_TYPE] = self._playerState.ItemType
if self._playerState.Item is not None:
track:Track = self._playerState.Item
attributes[ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN] = track.Uri
if track.Explicit:
attributes[ATTR_SPOTIFYPLUS_TRACK_IS_EXPLICIT] = track.Explicit
if track.Type == SpotifyMediaTypes.TRACK.value:
attributes[ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN] = track.UriOrigin
if len(track.Artists) > 0:
attributes[ATTR_SPOTIFYPLUS_ARTIST_URI] = track.Artists[0].Uri
# for tracks, use the album image if present.
if track.Album.ImageUrl is not None:
attributes[ATTR_SPOTIFYPLUS_NOWPLAYING_IMAGE_URL] = track.Album.ImageUrl
elif track.Type == SpotifyMediaTypes.EPISODE.value:
episode:Episode = self._playerState.Item
# for episodes, use the episode image if present; otherwise use the show image if present.
if episode.ImageUrl is not None:
attributes[ATTR_SPOTIFYPLUS_NOWPLAYING_IMAGE_URL] = episode.ImageUrl
if episode.Show.ImageUrl is not None:
attributes[ATTR_SPOTIFYPLUS_NOWPLAYING_IMAGE_URL] = episode.Show.ImageUrl
if self._playerState.Context is None:
attributes[ATTR_SPOTIFYPLUS_CONTEXT_URI] = episode.Show.Uri
attributes['media_context_content_id'] = episode.Show.Uri
if (self._playerState.CurrentlyPlayingType is not None):
attributes[ATTR_SPOTIFYPLUS_PLAYING_TYPE] = self._playerState.CurrentlyPlayingType
# add currently active device information.
if self._spotifyConnectDevice is not None:
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_BRAND_SONOS] = self._spotifyConnectDevice.IsSonos
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_CHROMECAST] = self._spotifyConnectDevice.IsChromeCast
attributes[ATTR_SPOTIFYPLUS_DEVICE_IS_RESTRICTED] = self._spotifyConnectDevice.IsRestricted
# add currently active playlist information.
if self._playlist is not None:
attributes[ATTR_SPOTIFYPLUS_PLAYLIST_NAME] = self._playlist.Name
attributes[ATTR_SPOTIFYPLUS_PLAYLIST_URI] = self._playlist.Uri
attributes['media_playlist_content_id'] = self._playlist.Uri
# add estimated play time remaining (if media position and duration are present).
if (self._attr_media_position is not None) and (self._attr_media_duration is not None):
# only update the state value with real-time info if in the last scan interval;
# otherwise, we will use the difference of the duration minus the last known position.
if (self._playTimeRemainingEst <= self._spotifyScanInterval):
if (self._attr_state == MediaPlayerState.PLAYING):
_logsi.LogVerbose("'%s': Estimated play time remaining is %s seconds; in last interval window" % (self.name, self._playTimeRemainingEst))
attributes[ATTR_SPOTIFYPLUS_PLAY_TIME_REMAINING_EST] = self._playTimeRemainingEst
else:
attributes[ATTR_SPOTIFYPLUS_PLAY_TIME_REMAINING_EST] = int(self._attr_media_duration - self._attr_media_position)
# add userprofile information.
if self.data.spotifyClient is not None:
profile:UserProfile = self.data.spotifyClient.UserProfile
if profile is not None:
attributes[ATTR_SPOTIFYPLUS_USER_COUNTRY] = profile.Country
attributes[ATTR_SPOTIFYPLUS_USER_DISPLAY_NAME] = profile.DisplayName
attributes[ATTR_SPOTIFYPLUS_USER_EMAIL] = profile.EMail
attributes[ATTR_SPOTIFYPLUS_USER_ID] = profile.Id
attributes[ATTR_SPOTIFYPLUS_USER_PRODUCT] = profile.Product
attributes[ATTR_SPOTIFYPLUS_USER_URI] = profile.Uri
attributes[ATTR_SPOTIFYPLUS_USER_HAS_WEB_PLAYER_CREDENTIALS] = self.data.spotifyClient.HasSpotifyWebPlayerCredentials
# return to caller.
return attributes
@property
def state(self) -> MediaPlayerState:
""" Return the playback state. """
return self._attr_state
@property
def media_content_id(self) -> str | None:
""" Return the media URL. """
return self._attr_media_content_id
@property
def media_content_type(self) -> str | None:
""" Return the media type. """
return self._attr_media_content_type
@property
def media_duration(self) -> int | None:
""" Duration of current playing media in seconds. """
return self._attr_media_duration
@property
def media_position(self) -> int | None:
""" Position of current playing media in seconds. """
return self._attr_media_position
@property
def media_position_updated_at(self) -> dt.datetime | None:
"""
When was the position of the current playing media valid.
"""
return self._attr_media_position_updated_at
@property
def media_image_url(self) -> str | None:
""" Return the media image URL. """
return self._attr_media_image_url
@property
def media_title(self) -> str | None:
""" Return the media title. """
return self._attr_media_title
@property
def media_artist(self) -> str | None:
""" Return the media artist. """
return self._attr_media_artist
@property
def media_album_name(self) -> str | None:
""" Return the media album. """
return self._attr_media_album_name
@property
def media_track(self) -> int | None:
""" Track number of current playing media, music track only. """
return self._attr_media_track
@property
def media_playlist(self):
""" Title of current playing playlist. """
if self._playlist is not None:
return self._playlist.Name
return None
@property
def media_playlist_content_id(self):
""" Content ID of current playing playlist. """
if self._playlist is not None:
return self._playlist.Uri
return None
@property
def media_playlist_content_type(self):
""" Content Type of current playing playlist. """
if self._playlist is not None:
return self._playlist.Type
return None
@property
def media_playlist_description(self):
""" Description of current playing playlist. """
if self._playlist is not None:
return self._playlist.Description
return None
@property
def media_playlist_image_url(self):
""" Image URL of current playing playlist. """
if self._playlist is not None:
return self._playlist.ImageUrl
return None
@property
def source(self) -> str | None:
""" Return the current playback device. """
return self._attr_source
@property
def source_list(self) -> list[str] | None:
""" Return a list of source devices. """
# ** IMPORTANT **
# be careful what `SpotifyConnectDirectory` methods you access from HA state properties!
# some methods use the `self._SpotifyConnectDevices_RLock`, which will cause a thread deadlock
# when trying to refresh oauth2 token!
# get Spotify Connect devices known to the local network.
result:SpotifyConnectDevices = self.data.spotifyClient.SpotifyConnectDirectory.GetDevices()
# get the list of device names to hide (omit) in the list.
sourceListHide:list = self.data.OptionSourceListHide
# build list of device names for the source list.
deviceNames:list[str] = []
device:PlayerDevice
for device in result.GetDeviceList():
if (device.Name.lower() not in sourceListHide):
if (device.Id.lower() not in sourceListHide):
deviceNames.append(device.Name)
return deviceNames
@property
def shuffle(self) -> bool | None:
"""Shuffling state."""
return self._attr_shuffle
@property
def repeat(self) -> RepeatMode | str | None:
""" Return current repeat mode. """
return self._attr_repeat
@property
def volume_level(self) -> float | None:
""" Volume level of the media player (0.0 to 1.0). """
return self._attr_volume_level
@property
def volume_step(self) -> float:
"""Return the step to be used by the volume_up and volume_down services."""
return self._attr_volume_step
@property
def is_volume_muted(self):
""" Boolean if volume is currently muted. """
return self._attr_is_volume_muted
@spotify_exception_handler
def media_play(self) -> None:
""" Start or resume playback. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_play")
# update ha state.
self._attr_state = MediaPlayerState.PLAYING
self.schedule_update_ha_state(force_refresh=False)
# call Spotify Web API to process the request.
_logsi.LogVerbose("'%s': Issuing command to Spotify Player: RESUME (source=\"%s\")" % (self.name, self._attr_source))
self.data.spotifyClient.PlayerMediaResume(deviceId=self._attr_source)
@spotify_exception_handler
def media_pause(self) -> None:
""" Pause playback. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_pause")
# update ha state.
self._attr_state = MediaPlayerState.PAUSED
self.schedule_update_ha_state(force_refresh=False)
# call Spotify Web API to process the request.
_logsi.LogVerbose("'%s': Issuing command to Spotify Player: PAUSE (source=\"%s\")" % (self.name, self._attr_source))
self.data.spotifyClient.PlayerMediaPause(deviceId=self._attr_source)
@spotify_exception_handler
def media_previous_track(self) -> None:
""" Skip to previous track. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_previous_track")
# call Spotify Web API to process the request.
_logsi.LogVerbose("'%s': Issuing command to Spotify Player: PREVIOUS (source=\"%s\")" % (self.name, self._attr_source))
self.data.spotifyClient.PlayerMediaSkipPrevious(deviceId=self._attr_source)
@spotify_exception_handler
def media_next_track(self) -> None:
""" Skip to next track. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "media_next_track")
# call Spotify Web API to process the request.
_logsi.LogVerbose("'%s': Issuing command to Spotify Player: NEXT (source=\"%s\")" % (self.name, self._attr_source))
self.data.spotifyClient.PlayerMediaSkipNext(deviceId=self._attr_source)
@spotify_exception_handler
def media_seek(self, position: float) -> None:
""" Send seek command. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS, self.name, "media_seek", "position='%s'" % (position))
# update ha state.
self._attr_media_position = int(position)
self._attr_media_position_updated_at = utcnow().replace(microsecond=0)
self.schedule_update_ha_state(force_refresh=False)
# call Spotify Web API to process the request.
_logsi.LogVerbose("'%s': Issuing command to Spotify Player: SEEK (position=%s)" % (self.name, position))
self.data.spotifyClient.PlayerMediaSeek(int(position * 1000), deviceId=self._attr_source)
@spotify_exception_handler
def mute_volume(self, mute:bool) -> None:
""" Send mute command. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS, self.name, "mute_volume", "mute='%s'" % (mute))
self._attr_is_volume_muted = mute
if mute:
self._volume_level_saved = self._attr_volume_level
self.schedule_update_ha_state(force_refresh=False)
self.set_volume_level(0.0)
else:
# did we save the volume on a previous mute request? if not, then default volume.
if self._volume_level_saved is None or self._volume_level_saved == 0.0:
_logsi.LogVerbose("Previously saved volume was not set; defaulting to 0.10")
self._volume_level_saved = 0.10
self._attr_volume_level = self._volume_level_saved
self.schedule_update_ha_state(force_refresh=False)
self.set_volume_level(self._volume_level_saved)
@spotify_exception_handler
def play_media(self, media_type: MediaType | str, media_id: str, **kwargs: Any) -> None:
"""
Play media; called by media browser when a browsed item is selected for playing.
"""
methodParms:SIMethodParmListContext = None
try:
# trace.
methodParms = _logsi.EnterMethodParmList(SILevel.Debug)
methodParms.AppendKeyValue("media_type", media_type)
methodParms.AppendKeyValue("media_id", media_id)
methodParms.AppendKeyValue("**kwargs", kwargs)
_logsi.LogMethodParmList(SILevel.Verbose, STAppMessages.MSG_MEDIAPLAYER_SERVICE % (self.name, "play_media"), methodParms)
# get enqueue keyword arguments (if any).
enqueue:MediaPlayerEnqueue = kwargs.get(ATTR_MEDIA_ENQUEUE, None)
# spotify can't handle URI's with query strings or anchors
# yet, they do generate those types of URI in their official clients.
media_id:str = str(URL(media_id).with_query(None).with_fragment(None))
# is this an enqueue request?
if enqueue is not None:
_logsi.LogVerbose("Enqueue command received for this play media request: '%s'" % (enqueue))
if enqueue == MediaPlayerEnqueue.ADD:
# add to queue request.
_logsi.LogVerbose("Adding uri to Spotify Player Queue: '%s'" % (media_id))
self.data.spotifyClient.AddPlayerQueueItems(media_id, deviceId=self._attr_source)
return
elif enqueue == MediaPlayerEnqueue.NEXT:
# play next request.
_logsi.LogVerbose("Playing next item in the player queue")
self.data.spotifyClient.PlayerMediaSkipNext(deviceId=self._attr_source)
return
elif enqueue == MediaPlayerEnqueue.PLAY:
# play request.
pass # we will handle this below.
elif enqueue == MediaPlayerEnqueue.REPLACE:
# play now and clear queue request.
pass # we will handle this below.
else:
_logsi.LogWarning("Spotify Connect Player does not support '%s' enqueue requests; request ignored" % enqueue)
return
# set current source device reference.
deviceId:str = self._attr_source
# start play based upon the media type.
if media_type in {MediaType.TRACK, MediaType.EPISODE, MediaType.MUSIC}:
self._attr_state = MediaPlayerState.PLAYING
self.schedule_update_ha_state(force_refresh=False)
_logsi.LogVerbose("Playing via PlayerMediaPlayTracks: uris='%s', deviceId='%s'" % (media_id, deviceId))
self.service_spotify_player_media_play_tracks(media_id, deviceId=deviceId)
elif media_type in PLAYABLE_MEDIA_TYPES:
self._attr_state = MediaPlayerState.PLAYING
self.schedule_update_ha_state(force_refresh=False)
_logsi.LogVerbose("Playing via PlayerMediaPlayContext: contextUri='%s', deviceId='%s'" % (media_id, deviceId))
self.service_spotify_player_media_play_context(media_id, deviceId=deviceId)
else:
_logsi.LogWarning("Media type '%s' is not supported" % media_type)
return
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
@spotify_exception_handler
def select_source(self, source: str) -> None:
""" Select playback device. """
try:
# trace.
methodParms = _logsi.EnterMethodParmList(SILevel.Debug)
methodParms.AppendKeyValue("source", source)
methodParms.AppendKeyValue("self.state", self._attr_state)
_logsi.LogMethodParmList(SILevel.Verbose, STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS % (self.name, "select_source", "source='%s'" % (source)), methodParms)
# was a source selected? if not, then we are done.
if (source is None) or (len(source) == 0):
raise ServiceValidationError("'%s': Source argument was not specified while trying to select a source)" % (self.name))
# are we currently powered off?
if self._attr_state == MediaPlayerState.OFF:
# power on the player.
# note that the `turn_on()` method will issue a transfer playback to the specified source.
self._source_at_poweron = source
self.turn_on()
self._isInCommandEvent = True # turn "in a command event" indicator back on.
else:
# resolve from device id.
fromDeviceId:str = self._attr_source
if (self._playerState is not None):
if (self._playerState.Device is not None):
fromDeviceId = self._playerState.Device.Id
# transfer playback to the specified source.
self.service_spotify_player_transfer_playback(
source,
play=self.data.OptionTurnOnAutoResume,
refreshDeviceList=True,
forceActivateDevice=True,
deviceIdFrom=fromDeviceId)
# set the selected source.
_logsi.LogVerbose("'%s': Selected source was changed to \"%s\"" % (self.name, self._attr_source))
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except HomeAssistantError: raise # pass handled exceptions on thru
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
@spotify_exception_handler
def set_shuffle(self, shuffle: bool) -> None:
""" Enable/Disable shuffle mode. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS, self.name, "set_shuffle", "shuffle='%s'" % (shuffle))
# update ha state.
self._attr_shuffle = shuffle
self.schedule_update_ha_state(force_refresh=False)
# set shuffle mode.
self.data.spotifyClient.PlayerSetShuffleMode(shuffle, deviceId=self._attr_source)
@spotify_exception_handler
def set_repeat(self, repeat: RepeatMode) -> None:
""" Set repeat mode. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS, self.name, "set_repeat", "repeat='%s'" % (repeat))
# update ha state.
if repeat not in REPEAT_MODE_MAPPING_TO_SPOTIFY:
raise ServiceValidationError(f"Unsupported repeat mode: {repeat}")
self._attr_repeat = repeat
self.schedule_update_ha_state(force_refresh=False)
# set repeat mode.
self.data.spotifyClient.PlayerSetRepeatMode(REPEAT_MODE_MAPPING_TO_SPOTIFY[repeat], deviceId=self._attr_source)
@spotify_exception_handler
def set_volume_level(self, volume: float) -> None:
""" Set the volume level. """
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE_WITH_PARMS, self.name, "set_volume_level", "volume='%s'" % (volume))
# validations.
if volume is None:
volume = 0.0
# update ha state.
self._attr_volume_level = volume
self.schedule_update_ha_state(force_refresh=False)
# call Spotify Web API to process the request.
self.data.spotifyClient.PlayerSetVolume(int(volume * 100), deviceId=self._attr_source)
@spotify_exception_handler
def turn_off(self) -> None:
""" Turn off media player. """
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "turn_off")
_logsi.LogVerbose("'%s': entity_id=%s" % (self.name, self.entity_id))
# save currently active source.
_logsi.LogVerbose("'%s': Active source at PowerOff: '%s'" % (self.name, self._attr_source))
self._source_at_poweroff = self._attr_source
# set media player state and update ha state.
self._attr_state = MediaPlayerState.OFF
_logsi.LogVerbose("'%s': MediaPlayerState set to '%s'" % (self.name, self._attr_state))
self.schedule_update_ha_state(force_refresh=False)
# pause playback.
if (self.data.spotifyClient.UserProfile.IsProductPremium) or (self.data.spotifyClient.HasSpotifyWebPlayerCredentials):
if self._playerState.IsPlaying:
if (self.data.OptionTurnOffAutoPause):
self.data.spotifyClient.PlayerMediaPause(deviceId=self._attr_source)
# call script to power off device.
self._CallScriptPower(self.data.OptionScriptTurnOff, "turn_off")
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex
finally:
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# trace.
_logsi.LeaveMethod(SILevel.Debug)
@spotify_exception_handler
def turn_on(self) -> None:
""" Turn on media player. """
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogVerbose(STAppMessages.MSG_MEDIAPLAYER_SERVICE, self.name, "turn_on")
# if already powered on then we are done.
if self._attr_state is not MediaPlayerState.OFF:
_logsi.LogVerbose("'%s': MediaPlayer is already powered on (state=%s); nothing to do" % (self.name, self._attr_state))
return
# set media player state to IDLE.
self._attr_state = MediaPlayerState.IDLE
_logsi.LogVerbose("'%s': MediaPlayerState set to '%s'" % (self.name, self._attr_state))
# call script to power on device.
self._CallScriptPower(self.data.OptionScriptTurnOn, "turn_on")
# just in case the OptionScriptTurnOn reset the state, set media player state to IDLE.
self._attr_state = MediaPlayerState.IDLE
# get current Spotify Connect device player state.
self._playerState = self.data.spotifyClient.GetDevicePlaybackState(deviceId=self._attr_source)
# are we automatically selecting a source at turn on?
if (self.data.OptionTurnOnAutoSelectSource):
# is this a spotify premium account?
if (self.data.spotifyClient.UserProfile.IsProductPremium) or (self.data.spotifyClient.HasSpotifyWebPlayerCredentials):
# try to automatically select a source for play, in this order:
# 1) Source at power on, if there is one (set in source_select if powered off).
# 2) Currently active Spotify Connect device source, if there is one.
# 3) Source at last power off, if there is one.
# 4) SpotifyPlus configuration option default device, if there is one.
# otherwise, we cannot automatically select a source!
_logsi.LogVerbose("'%s': Selecting initial source at power on" % (self.name))
source:str = None
if (self._source_at_poweron is not None):
source = self._source_at_poweron
_logsi.LogVerbose("'%s': source_select at power on will be selected: \"%s\"" % (self.name, source))
self._source_at_poweron = None
elif (self._playerState is not None) and (self._playerState.Device is not None) and (self._playerState.Device.Name is not None):
source = self._playerState.Device.Name
_logsi.LogVerbose("'%s': Currently active Spotify Connect device source will be selected: \"%s\"" % (self.name, source))
elif (self._source_at_poweroff is not None):
source = self._source_at_poweroff
_logsi.LogVerbose("'%s': Source at last power off will be selected: \"%s\"" % (self.name, source))
elif (self.data.OptionDeviceDefault is not None):
source = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)
if (source is None):
source = PlayerDevice.GetNameFromSelectItem(self.data.OptionDeviceDefault)
_logsi.LogVerbose("'%s': SpotifyPlus configuration option default device will be selected: \"%s\"" % (self.name, source))
else:
_logsi.LogVerbose("'%s': Could not auto-select a source for play" % (self.name))
# was a source selected?
if source is not None:
# yes - transfer playback to the source.
self.select_source(source)
self._isInCommandEvent = True # turn "in a command event" indicator back on.
# trace.
_logsi.LogObject(SILevel.Verbose, "'%s': About to resume play; Last known media content: %s" % (self.name, self.data.spotifyClient.PlayerLastPlayedInfo.Summary), self.data.spotifyClient.PlayerLastPlayedInfo, excludeNonPublic=True)
# is playing content paused? if so, then resume play.
if (self._playerState.Device.IsActive) \
and (self._playerState.Actions.Pausing) \
and (self.data.OptionTurnOnAutoResume):
_logsi.LogVerbose("'%s': MediaPlayer turned on - resuming play on source device: '%s'" % (self.name, self._playerState.Device.Name))
self.media_play()
self._isInCommandEvent = True # turn "in a command event" indicator back on.
else:
# trace.
_logsi.LogVerbose("'%s': Spotify account is not Premium; could not transfer playback, nor resume play" % (self.name))
else:
# trace.
_logsi.LogVerbose("'%s': Selecting initial source at power on was disabled; Spotify PlayerState will determine source" % (self.name))
# trace.
_logsi.LogVerbose("'%s': MediaPlayer turn_on complete" % (self.name))
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex
finally:
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# trace.
_logsi.LeaveMethod(SILevel.Debug)
def update(self) -> None:
""" Update state and attributes. """
# trace.
_logsi.WatchDateTime(SILevel.Debug, "HASpotifyUpdateLastDT", datetime.now())
# is the media player enabled? if not, then there is nothing to do.
if not self.enabled:
return
# is the media player powered off? if so, then there is nothing to do.
if self._attr_state == MediaPlayerState.OFF:
return
# is the media player in a command event? if so, then exit as updates are
# happening that we don't want overridden just yet.
if self._isInCommandEvent:
_logsi.LogVerbose("'%s': Update - Integration is in a command event; bypassing update" % self.name)
return
# is the media player still processing a previous update event? if so, then exit
# as we want to avoid generating log file entries for updates that are taking longer
# than expected (which are usually due to temporary conditions anyway).
if self._isInUpdateEvent:
_logsi.LogVerbose("'%s': Update - Integration is still processing a previous update event; bypassing current update" % self.name, colorValue=SIColors.Red)
return
# is the authentication token being refreshed? if so, then exit as updates are
# happening that we don't want to interfere with.
if self.data.tokenUpdater_lock.locked():
_logsi.LogVerbose("'%s': Update - Integration is refreshing authentication token; bypassing update" % self.name, colorValue=SIColors.Gold)
return
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogVerbose("'%s': Scan interval %d check - commandScanInterval=%d, currentScanInterval=%d, playTimeRemainingEst=%d, state=%s" % (self.name, self._spotifyScanInterval, self._commandScanInterval, self._currentScanInterval, self._playTimeRemainingEst, str(self._attr_state)))
# have we reached a scan interval?
if (self._currentScanInterval == self._spotifyScanInterval) \
or (not ((self._currentScanInterval % self._spotifyScanInterval) == 0)):
# if last known time remaining value is less than current scan interval then
# use the lesser last known time remaining value as the current scan interval.
# this can happen when a user seeks to a new position, and the track time
# remaining is less than the current scan interval. we will also check the
# last known time remaining value for greater than zero, as it is zero at startup.
if (self._lastKnownTimeRemainingSeconds > 0) \
and (self._lastKnownTimeRemainingSeconds < self._currentScanInterval) \
and (self._attr_state == MediaPlayerState.PLAYING):
self._currentScanInterval = self._lastKnownTimeRemainingSeconds
_logsi.LogVerbose("'%s': Resetting current scan interval to last known time remaining value - currentScanInterval=%d, lastKnownTimeRemainingSeconds=%d, playTimeRemainingEst=%d, state=%s" % (self.name, self._currentScanInterval, self._lastKnownTimeRemainingSeconds, self._playTimeRemainingEst, str(self._attr_state)))
# do we need to query Spotify for player state?
if (self._currentScanInterval == 0):
# yes - we reached a scan interval (e.g. 30 seconds).
_logsi.LogVerbose("'%s': updating playerstate - current scan interval is zero" % (self.name))
pass
elif (self._commandScanInterval != 0):
# yes - we are monitoring an issued command response (e.g. 5 seconds).
_logsi.LogVerbose("'%s': updating playerstate - monitoring an issued command response" % (self.name))
pass
elif ((self._lastKnownTimeRemainingSeconds <= SPOTIFY_SCAN_INTERVAL_TRACK_ENDSTART) and (self._attr_state == MediaPlayerState.PLAYING)):
# yes - allow the update
_logsi.LogVerbose("'%s': updating playerstate - monitoring for end of track (remaining track time is nearing zero)" % (self.name))
pass
else:
# no - keep waiting to update.
# decrement the current scan interval count.
self._currentScanInterval = self._currentScanInterval - 1
# are we playing content?
if (self._attr_state == MediaPlayerState.PLAYING):
# add scan interval time value to last media played position since it's not a real-time value.
self._lastMediaPlayedPosition = (self._lastMediaPlayedPosition + SCAN_INTERVAL.seconds)
# does the media position have a last update date? if so, then calculate play time remaining.
if (isinstance(self._attr_media_position_updated_at, datetime)):
# calculate play time remaining by subtracting current UTC time from
# the last UTC time when the media_position was provided by Spotify player state.
dtUtc:datetime = utcnow().replace(microsecond=0)
timeDifference:timedelta = (dtUtc - self._attr_media_position_updated_at)
self._playTimeRemainingEst = int(self._attr_media_duration - self._attr_media_position - int(timeDifference.total_seconds()))
#_logsi.LogVerbose("'%s': Estimated time remaining (timeDifference) - media Duration=%d, Position=%s, Remaining=%d, TimeDiffSecs=%d, state=%s" % (self.name, int(self._attr_media_duration), int(self._attr_media_position), self._playTimeRemainingEst, timeDifference.total_seconds(), str(self._attr_state)))
# bypass HA state update.
return
# # TEST TODO - force token expire!!!
# leave the following comments in here, in case you need to test token expire logic.
# this will force the token to expire in 30 seconds, instead of the 1 hour Spotify default.
#
# _logsi.LogWarning("TEST TODO - Forcing token expiration in 30 seconds for testing purposes", colorValue=SIColors.Red)
# self.data.spotifyClient.AuthToken._ExpiresIn = 30
# unix_epoch = datetime(1970, 1, 1)
# dtUtcNow:datetime = datetime.utcnow()
# self.data.spotifyClient.AuthToken._ExpireDateTimeUtc = dtUtcNow + timedelta(seconds=self.data.spotifyClient.AuthToken._ExpiresIn)
# self.data.spotifyClient.AuthToken._ExpiresAt = int((dtUtcNow - unix_epoch).total_seconds()) # seconds from epoch, current date
# self.data.spotifyClient.AuthToken._ExpiresAt = self.data.spotifyClient.AuthToken._ExpiresAt + self.data.spotifyClient.AuthToken._ExpiresIn # add ExpiresIn seconds
# indicate we are updating status.
self._isInUpdateEvent = True
# are we monitoring a command response? if so, then decrement the interval count.
if self._commandScanInterval > 0:
self._commandScanInterval = self._commandScanInterval - 1
# get now playing status.
_logsi.LogVerbose("'%s': update method - getting Spotify Connect device player state" % self.name)
self._playerState = self.data.spotifyClient.GetDevicePlaybackState(deviceId=self._attr_source)
self._UpdateHAFromPlayerPlayState(self._playerState)
_logsi.WatchDateTime(SILevel.Debug, "HASpotifyPlaystateLastUpdate", datetime.now())
# update the scan interval for next time.
self._currentScanInterval = self._spotifyScanInterval
# did the now playing context change?
context:Context = self._playerState.Context
if (context is not None) and (self._playlist is None or self._playlist.Uri != context.Uri):
# yes - if it's a playlist, then we need to update the stored playlist reference.
self._playlist = None
if context.Type == MediaType.PLAYLIST:
# as of 2024/11/27, Spotify deprecated API support for various Spotify-owned playlists!
# due to that, the following `GetPlaylist` call will fail if the currently playing context
# is a Spotify-owned "algorithmic" playlist (e.g. various "Made For You" content, etc).
try:
_logsi.LogVerbose("'%s': Retrieving playlist for context uri '%s'" % (self.name, context.Uri))
spotifyId:str = SpotifyClient.GetIdFromUri(context.Uri)
self._playlist = self.data.spotifyClient.GetPlaylist(spotifyId)
except Exception as ex:
#_logsi.LogException("Unable to get playlist data for context '%s'. Continuing without playlist data" % context.Uri, ex, logToSystemLogger=False)
_logsi.LogWarning("'%s': Unable to get playlist data for context '%s'. Continuing without playlist data. GetPlaylist response: %s" % (self.name, context.Uri, str(ex)), logToSystemLogger=False)
# if we could not get the current playlist info, then build a "dummy" playlist so that
# information is still conveyed in the extended attributes.
self._playlist = Playlist()
self._playlist.Uri = context.Uri
self._playlist.Type = self.data.spotifyClient.GetTypeFromUri(context.Uri)
self._playlist.Id = self.data.spotifyClient.GetIdFromUri(context.Uri)
self._playlist.Name = "Unknown"
self._playlist.Description = str(ex)
else:
self._playlist = None
elif (context is None):
self._playlist = None
except SpotifyWebApiError as ex:
_logsi.LogException(None, ex)
raise ServiceValidationError(ex.Message) from ex
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex
finally:
# indicate we are no longer updating status.
self._isInUpdateEvent = False
# trace.
_logsi.LeaveMethod(SILevel.Debug)
def _AutoPowerOnCheck(self) -> None:
"""
Checks to see if media player state is OFF, and will switch state to ON if so.
Note that this does not engage the `turn_on` method, so the turn on script is
not called, no source switching, etc.
This should only be called for services that successfully initiate play in some
way (e.g. PlayerMediaPlayContext, PlayerMediaPlayTracks, Resume, Play, etc).
"""
if self._attr_state is MediaPlayerState.OFF:
_logsi.LogVerbose("'%s': MediaPlayer is automatically turning on due to a successful transport command; turn_on method was not called" % (self.name))
self._attr_state = MediaPlayerState.ON
def _UpdateHAFromPlayerPlayState(
self,
playerPlayState:PlayerPlayState,
) -> None:
"""
Updates all media_player attributes that have to do with now playing information.
"""
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogObject(SILevel.Verbose, "'%s': Updating HA state from Spotify PlayerPlayState object" % self.name, playerPlayState, excludeNonPublic=True)
# initialize media attributes.
self._attr_media_album_name = None
self._attr_media_artist = None
self._attr_media_content_id = None
self._attr_media_content_type = None
self._attr_media_duration = None
self._attr_media_image_url = None
self._attr_media_position = None
self._attr_media_position_updated_at = None
self._attr_media_title = None
self._attr_media_track = None
self._attr_repeat = None
self._attr_shuffle = None
self._attr_volume_level = None
self._attr_is_volume_muted = None
# initialize supported features.
if (self.data.spotifyClient.UserProfile.IsProductPremium):
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.NEXT_TRACK
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.PREVIOUS_TRACK
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.REPEAT_SET
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.SEEK
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.SHUFFLE_SET
elif (self.data.spotifyClient.HasSpotifyWebPlayerCredentials):
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.NEXT_TRACK
self._attr_supported_features = self._attr_supported_features | MediaPlayerEntityFeature.REPEAT_SET
# does player state exist? if not, then we are done.
if playerPlayState is None:
_logsi.LogVerbose("'%s': Spotify PlayerPlayState object was not set; nothing to do" % self.name)
return
# if player is not OFF, then update media player state.
if self._attr_state is not MediaPlayerState.OFF:
if self._isInCommandEvent:
pass
elif (playerPlayState.IsEmpty):
self._attr_state = MediaPlayerState.IDLE
elif playerPlayState.IsPlaying == True:
self._attr_state = MediaPlayerState.PLAYING
elif playerPlayState.IsPlaying == False:
self._attr_state = MediaPlayerState.PAUSED
else:
self._attr_state = MediaPlayerState.IDLE
_logsi.LogVerbose("'%s': MediaPlayerState set to '%s'" % (self.name, self._attr_state))
self._attr_is_volume_muted = playerPlayState.IsMuted
self._attr_shuffle = playerPlayState.ShuffleState
# update item-related attributes (e.g. track? episode? etc)?
if playerPlayState.Item is not None:
item = playerPlayState.Item
episode:Episode = playerPlayState.Item
self._attr_media_content_id = item.Uri
self._attr_media_content_type = item.Type
self._attr_media_duration = int(item.DurationMS / 1000)
self._attr_media_title = item.Name
# update media album name attribute.
if item.Type == MediaType.EPISODE.value:
self._attr_media_album_name = episode.Show.Name
else:
self._attr_media_album_name = item.Album.Name
# update media artist attribute.
if item.Type == MediaType.EPISODE.value:
self._attr_media_artist = episode.Show.Publisher
else:
self._attr_media_artist = ", ".join(artist.Name for artist in item.Artists)
# update media content type attribute.
if item.Type == MediaType.EPISODE.value:
self._attr_media_content_type = MediaType.PODCAST
else:
self._attr_media_content_type = MediaType.MUSIC
# update media image url attribute.
# for episodes, use the episode image if present; otherwise use the show image if present.
# for everything else, use the album image if present.
if item.Type == MediaType.EPISODE.value:
if episode.ImageUrl is not None:
self._attr_media_image_url = episode.ImageUrl
if episode.Show.ImageUrl is not None:
self._attr_media_image_url = episode.Show.ImageUrl
elif item.Album.ImageUrl is not None:
self._attr_media_image_url = item.Album.ImageUrl
# update media track attribute (will not exist for episodes).
if item.Type == MediaType.TRACK.value:
self._attr_media_track = item.TrackNumber
# update device-related attributes.
if playerPlayState.Device is not None:
device = playerPlayState.Device
self._attr_volume_level = float(device.VolumePercent / 100)
if (self._attr_source is None):
# if a source is not set, then use current source (if source is active).
if (self._attr_state in [MediaPlayerState.PLAYING, MediaPlayerState.PAUSED, MediaPlayerState.IDLE, MediaPlayerState.BUFFERING]):
_logsi.LogVerbose("'%s': Source value is not set; using Spotify Web API player name (\"%s\")" % (self.name, playerPlayState.Device.Name))
self._attr_source = playerPlayState.Device.Name
elif (self._attr_source != playerPlayState.Device.Name) and (playerPlayState.Device.Name is not None):
# if a source is set, but does not match the currently playing source, then
# force it to be the currently playing source.
# this seems to happen a lot for Sonos devices!
_logsi.LogVerbose("'%s': Source value (\"%s\") does not match Spotify Web API player name (\"%s\"); using Spotify Web API player name" % (self.name, self._attr_source, playerPlayState.Device.Name))
self._attr_source = playerPlayState.Device.Name
# check to see if currently active device is in the Spotify Connect device list cache.
# if it's not in the cache, then we need to refresh the Spotify Connect device list cache.
scDevices:SpotifyConnectDevices = self.data.spotifyClient.GetSpotifyConnectDevices(refresh=False)
if not scDevices.ContainsDeviceName(playerPlayState.Device.Name):
_logsi.LogVerbose("'%s': Spotify PlayerPlayState device name \"%s\" was not found in the Spotify Connect device list cache; refreshing cache" % (self.name, self._attr_source))
self.data.spotifyClient.GetSpotifyConnectDevices(refresh=True)
# update seek-related attributes.
# also save currently playing track position in case we need to restore it later.
if playerPlayState.ProgressMS is not None:
self._attr_media_position = int(playerPlayState.ProgressMS / 1000)
self._attr_media_position_updated_at = utcnow().replace(microsecond=0)
self._lastMediaPlayedPosition = self._attr_media_position
# calculate the time (in seconds) remaining on the playing track.
if (self._attr_media_position is not None) and (self._attr_media_duration is not None):
self._lastKnownTimeRemainingSeconds = int(self._attr_media_duration - self._attr_media_position)
self._playTimeRemainingEst = int(self._attr_media_duration - self._attr_media_position)
_logsi.LogVerbose("'%s': Estimated time remaining (playstateUpdate) - media Duration=%d, Position=%s, Remaining=%d" % (self.name, int(self._attr_media_duration), int(self._attr_media_position), self._playTimeRemainingEst))
# update repeat related attributes.
if playerPlayState.RepeatState is not None:
if playerPlayState.RepeatState == 'context':
self._attr_repeat = RepeatMode.ALL.value
elif playerPlayState.RepeatState == 'track':
self._attr_repeat = RepeatMode.ONE.value
else:
self._attr_repeat = RepeatMode.OFF.value
# update supported features based upon available actions.
# note if the selected action is true, then it is not allowed (disallowed).
if (self.data.spotifyClient.UserProfile.IsProductPremium):
if playerPlayState.Actions is not None:
# 2024/07/24
# commented some of the following, as some media players (e.g. mini-media player) do not
# display / hide the corresponding transport button correctly!
# if playerPlayState.Actions.SkippingNext:
# self._attr_supported_features = self._attr_supported_features & (~MediaPlayerEntityFeature.NEXT_TRACK)
# _logsi.LogVerbose("'%s': MediaPlayerState NEXT_TRACK is not allowed" % self.name)
# if playerPlayState.Actions.SkippingPrev:
# self._attr_supported_features = self._attr_supported_features & (~MediaPlayerEntityFeature.PREVIOUS_TRACK)
# _logsi.LogVerbose("'%s': MediaPlayerState PREVIOUS_TRACK is not allowed" % self.name)
if playerPlayState.Actions.TogglingRepeatTrack:
self._attr_supported_features = self._attr_supported_features & (~MediaPlayerEntityFeature.REPEAT_SET)
_logsi.LogVerbose("'%s': MediaPlayerState REPEAT_SET is not allowed" % self.name)
# if playerPlayState.Actions.Seeking:
# self._attr_supported_features = self._attr_supported_features & (~MediaPlayerEntityFeature.SEEK)
# _logsi.LogVerbose("'%s': MediaPlayerState SEEK is not allowed" % self.name)
if playerPlayState.Actions.TogglingShuffle:
self._attr_supported_features = self._attr_supported_features & (~MediaPlayerEntityFeature.SHUFFLE_SET)
_logsi.LogVerbose("'%s': MediaPlayerState SHUFFLE_SET is not allowed" % self.name)
pass
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
def _CallScriptPower(self, scriptEntityId:str, title:str) -> None:
"""
Calls the supplied script for a power on / off event.
Args:
scriptEntityId (str):
Name of the script to execute.
title (str):
Title to use in trace logs and error messages.
Verifies that the script is installed, and the media player entity id
is valid and available (not disabled).
"""
entity_registry:EntityRegistry = None
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogVerbose("'%s': MediaPlayer is verifying SpotifyPlus '%s' integration script configuration" % (self.name, title))
# TEST TODO uncomment as needed, as this logs a LOT of information!
# if SpotifyPlus integration is NOT installed, then log the services that ARE installed in case we need it.
#serviceAll = self.hass.services.async_services()
#_logsi.LogDictionary(SILevel.Verbose, "'%s': MediaPlayer ALL services list" % self.name, serviceAll, prettyPrint=True)
# if no script name was selected in config options then there is nothing else to do.
if scriptEntityId is None:
_logsi.LogVerbose("'%s': MediaPlayer '%s' script is not configured - nothing to do" % (self.name, title))
return
# is the specified script entity id in the hass entity registry?
# it will NOT be in the entity registry if it's deleted.
# it WILL be in the entity registry if it is disabled, with disabled property = True.
entity_registry = er.async_get(self.hass)
registry_entry:RegistryEntry = entity_registry.async_get(scriptEntityId)
_logsi.LogObject(SILevel.Verbose, "'%s': MediaPlayer RegistryEntry for entity_id: '%s'" % (self.name, scriptEntityId), registry_entry)
# raise exceptions if SpotifyPlus Entity is not configured or is disabled.
if registry_entry is None:
raise ServiceValidationError("'%s': MediaPlayer '%s' script entity '%s' does not exist (recently deleted or renamed maybe?)" % (self.name, title, scriptEntityId))
if registry_entry.disabled:
raise ServiceValidationError("'%s': MediaPlayer '%s' script entity '%s' is currently disabled; re-enable the script to continue" % (self.name, title, scriptEntityId))
# call the script syncronously, so we wait until it returns.
_logsi.LogVerbose("'%s': MediaPlayer is calling the '%s' script '%s' (entityid='%s', uniqueid='%s')" % (self.name, title, registry_entry.name or registry_entry.original_name, registry_entry.entity_id, registry_entry.unique_id))
self.hass.services.call(
DOMAIN_SCRIPT,
registry_entry.unique_id, # use the uniqueid
{}, # no parameters
blocking=True, # wait for service to complete before returning
return_response=False # does not return service response data.
)
finally:
# free resources.
entity_registry = None
# trace.
_logsi.LeaveMethod(SILevel.Debug)
# -----------------------------------------------------------------------------------
# Custom Services
# -----------------------------------------------------------------------------------
def _GetUserProfilePartialDictionary(self, userProfile:UserProfile) -> dict:
"""
Returns a dictionary of a partial UserProfile object that can be returned with
selected service results. This allows the caller to know exactly what Spotify
user made the given request.
"""
# return the user profile that retrieved the result, as well as the result.
return {
"country": userProfile.Country,
"display_name": userProfile.DisplayName,
"email": userProfile.EMail,
"id": userProfile.Id,
"product": userProfile.Product,
"type": userProfile.Type,
"uri": userProfile.Uri,
}
def service_spotify_add_player_queue_items(
self,
uris:str,
deviceId:str=None,
verifyDeviceId:bool=True,
delay:float=0.15,
) -> None:
"""
Add one or more items to the end of the user's current playback queue.
Args:
uris (str):
A list of Spotify track or episode URIs to add to the queue; can be track or episode URIs.
All URIs must be of the same type - you cannot mix and match tracks and episodes!
Example: [`spotify:track:6zd8T1PBe9JFHmuVnurdRp` ,`spotify:track:1kWUud3vY5ij5r62zxpTRy`].
It can also be specified as a comma-delimited string.
Example: `spotify:track:6zd8T1PBe9JFHmuVnurdRp,spotify:track:1kWUud3vY5ij5r62zxpTRy`.
An unlimited number of items can be added in one request, but the more items the longer it
will take.
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
verifyDeviceId (bool):
DEPRECATED - deviceId is automatically verified as of v1.0.149.
True to verify a device id is active; otherwise, false to assume that a
device id is already active.
Default is True.
delay (float):
Time delay (in seconds) to wait AFTER issuing the add request. This delay will give the
Spotify web api time to process the queue change before another command is issued.
Default is 0.15; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_add_player_queue_items'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uris", uris)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("verifyDeviceId (DEPRECATED)", verifyDeviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Add Player Queue Items Service", apiMethodParms)
# follow artist(s).
_logsi.LogVerbose("Adding items(s) to Spotify Player Queue")
self.data.spotifyClient.AddPlayerQueueItems(uris, deviceId, verifyDeviceId, delay)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_album_favorites(
self,
ids:str=None,
) -> None:
"""
Check if one or more albums (or the currently playing album) exists in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the albums.
Maximum: 40 IDs.
Example: `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`
If null, the currently playing track album uri id value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the album is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_album_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Album Favorites Service", apiMethodParms)
# check Spotify album favorites.
_logsi.LogVerbose("Check Spotify Album Favorites")
result = self.data.spotifyClient.CheckAlbumFavorites(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_artists_following(
self,
ids:str=None,
) -> None:
"""
Check if one or more artists (or the currently playing artist) is followed in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the artists.
Maximum: 40 IDs.
Example: `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`
If null, the currently playing track artist uri id value is used.
Returns:
A dictionary of the IDs, along with a boolean status for each that indicates
if the user follows the ID (True) or not (False).
"""
apiMethodName:str = 'service_spotify_check_artists_following'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Follow Artists Service", apiMethodParms)
# check Spotify artists following.
_logsi.LogVerbose("Check Spotify Artists Following")
result = self.data.spotifyClient.CheckArtistsFollowing(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_audiobook_favorites(
self,
ids:str=None,
) -> None:
"""
Check if one or more audiobooks (or the currently playing audiobook) exists in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the audiobooks.
Maximum: 40 IDs.
Example: `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`
If null, the currently playing audiobook uri id value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the audiobook is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_audiobook_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Audiobook Favorites Service", apiMethodParms)
# check Spotify audiobook favorites.
_logsi.LogVerbose("Check Spotify Audiobook Favorites")
result = self.data.spotifyClient.CheckAudiobookFavorites(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_episode_favorites(
self,
ids:str=None,
) -> None:
"""
Check if one or more episodes (or the currently playing episode) exists in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the episodes.
Maximum: 40 IDs.
Example: `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`
If null, the currently playing episode uri id value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the episode is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_episode_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Episode Favorites Service", apiMethodParms)
# check Spotify episode favorites.
_logsi.LogVerbose("Check Spotify Episode Favorites")
result = self.data.spotifyClient.CheckEpisodeFavorites(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_playlist_followers(
self,
playlistId:str=None,
userIds:str=None,
) -> None:
"""
Check to see if the current user is following a specified playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `3cEYpjA9oz9GiPac4AsH4n`
userIds (str):
A comma-separated list of Spotify User ID's to check.
Maximum: 5 ID's.
Example: `1kWUud3vY5ij5r62zxpTRy,2takcwOaAZWiXQijPHIx7B`
Deprecated - A single item list containing current user's Spotify Username; Maximum of 1 id.
Returns:
Array of boolean, containing a single boolean status that indicates
if the user follows the playlist (True) or not (False).
"""
apiMethodName:str = 'service_spotify_check_playlist_followers'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("userIds", userIds)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Playlist Followers Service", apiMethodParms)
# validations.
if (userIds is None):
userIds = self.data.spotifyClient.UserProfile.Id
# check Spotify playlist followers.
_logsi.LogVerbose("Check Spotify Playlist Followers")
result = self.data.spotifyClient.CheckPlaylistFollowers(playlistId, userIds)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_show_favorites(
self,
ids:str=None,
) -> None:
"""
Check if one or more shows (or the currently playing show) exists in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the shows.
Maximum: 40 IDs.
Example: `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`
If null, the currently playing show uri id value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the show is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_show_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Show Favorites Service", apiMethodParms)
# check Spotify show favorites.
_logsi.LogVerbose("Check Spotify Show Favorites")
result = self.data.spotifyClient.CheckShowFavorites(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_track_favorites(
self,
ids:str=None,
) -> None:
"""
Check if one or more tracks (or the currently playing track) exists in the current
user's 'Your Library' favorites.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the tracks.
Maximum: 40 IDs.
Example: `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`
If null, the currently playing context uri id value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the track is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_track_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Track Favorites Service", apiMethodParms)
# check Spotify track favorites.
_logsi.LogVerbose("Check Spotify Track Favorites")
result = self.data.spotifyClient.CheckTrackFavorites(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_user_favorites(
self,
uris:str=None,
) -> None:
"""
Check if one or more items are already saved in the current user's library.
Accepts Spotify URIs for tracks, albums, episodes, shows, audiobooks, artists, users, and playlists.
Args:
uris (str):
A comma-separated list of the Spotify URIs for the items.
Maximum: 40 URIs.
Example: `spotify:artist:6APm8EjxOHSYM5B4i3vT3q,spotify:album:6vc9OTcyd3hyzabCmsdnwE,spotify:track:1kWUud3vY5ij5r62zxpTRy`
If null, the currently playing item uri value is used.
Returns:
A dictionary of the ids, along with a boolean status for each that indicates
if the item is saved (True) in the users 'Your Library' or not (False).
"""
apiMethodName:str = 'service_spotify_check_user_favorites'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uris", uris)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check User Favorites Service", apiMethodParms)
# check Spotify user favorites.
_logsi.LogVerbose("Check Spotify User Favorites")
result = self.data.spotifyClient.CheckUserFavorites(uris)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_check_users_following(
self,
ids:str,
) -> None:
"""
Check to see if the current user is following one or more users.
Args:
ids (str):
A comma-separated list of Spotify user ID's to check.
Maximum: 50 ID's.
Example: `smedjan`
Returns:
A dictionary of the IDs, along with a boolean status for each that indicates
if the user follows the ID (True) or not (False).
"""
apiMethodName:str = 'service_spotify_check_users_following'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Check Users Following Service", apiMethodParms)
# check Spotify users following.
_logsi.LogVerbose("Check Spotify Users Following")
result = self.data.spotifyClient.CheckUsersFollowing(ids)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_follow_artists(
self,
ids:str=None,
) -> None:
"""
Add the current user as a follower of one or more artists.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the artists.
Maximum: 40 IDs.
Example: `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`
If null, the currently playing track artist uri id value is used.
"""
apiMethodName:str = 'service_spotify_follow_artists'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Follow Artists Service", apiMethodParms)
# follow artist(s).
_logsi.LogVerbose("Adding items(s) to Spotify Artist Favorites")
self.data.spotifyClient.FollowArtists(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_follow_playlist(
self,
playlistId:str=None,
public:bool=True,
) -> None:
"""
Add the current user as a follower of a playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `3cEYpjA9oz9GiPac4AsH4n`
If null, the currently playing playlist uri id value is used.
public (bool):
If true the playlist will be included in user's public playlists, if false it
will remain private.
Default = True.
"""
apiMethodName:str = 'service_spotify_follow_playlist'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("public", public)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Follow Playlist Service", apiMethodParms)
# follow playlist.
_logsi.LogVerbose("Adding items to Spotify Playlist Favorites")
self.data.spotifyClient.FollowPlaylist(playlistId, public)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_follow_users(
self,
ids:str=None,
) -> None:
"""
Add the current user as a follower of one or more users.
Args:
ids (str):
A comma-separated list of the Spotify user IDs.
A maximum of 50 IDs can be sent in one request.
Example: `smedjan`
"""
apiMethodName:str = 'service_spotify_follow_users'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Follow Artists Service", apiMethodParms)
# follow user(s).
_logsi.LogVerbose("Adding items(s) to Spotify User Favorites")
self.data.spotifyClient.FollowUsers(ids)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_album(
self,
albumId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single album.
Args:
albumId (str):
The Spotify ID of the album.
Example: `6vc9OTcyd3hyzabCmsdnwE`
If omitted, the currently playing album uri id value is used.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `Album` object that contains the album details.
"""
apiMethodName:str = 'service_spotify_get_album'
apiMethodParms:SIMethodParmListContext = None
result:Album = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("albumId", albumId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Album Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetAlbum(albumId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_album_favorites(
self,
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of the albums saved in the current Spotify user's 'Your Library'.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `AlbumPageSaved` object that contains album information.
"""
apiMethodName:str = 'service_spotify_get_album_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Album Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:AlbumPageSaved = self.data.spotifyClient.GetAlbumFavorites(limit, offset, market, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_album_new_releases(
self,
limit:int=20,
offset:int=0,
country:str=None,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of new album releases featured in Spotify.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
country (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `AlbumPageSimplified` object that contains album information.
"""
apiMethodName:str = 'service_spotify_get_album_new_releases'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("country", country)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Album New Releases Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:AlbumPageSimplified = self.data.spotifyClient.GetAlbumNewReleases(limit, offset, country, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_album_tracks(
self,
albumId:str=None,
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None
) -> dict:
"""
Get Spotify catalog information about an album's tracks.
Args:
albumId (str):
The Spotify ID of the album.
Example: `6vc9OTcyd3hyzabCmsdnwE`
If null, the currently playing album uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
limit (int):
The maximum number of items to return in a page of items when manual paging is used.
Default: 20, Range: 1 to 50.
See the `limitTotal` argument for automatic paging option.
offset (int):
The index of the first item to return; use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `TrackPageSimplified` object that contains album track information.
"""
apiMethodName:str = 'service_spotify_get_album_tracks'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("albumId", albumId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Album Tracks Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:TrackPageSimplified = self.data.spotifyClient.GetAlbumTracks(albumId, limit, offset, market, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artist(
self,
artistId:str=None,
) -> dict:
"""
Get Spotify catalog information for a single artist.
Args:
artistId (str):
The Spotify ID of the artist.
If null, the currently playing artist uri id value is used.
Example: `6APm8EjxOHSYM5B4i3vT3q`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `Artist` object that contains the artist details.
"""
apiMethodName:str = 'service_spotify_get_artist'
apiMethodParms:SIMethodParmListContext = None
result:Artist = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("artistId", artistId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artist Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetArtist(artistId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artist_albums(
self,
artistId:str,
include_groups:str='album',
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get Spotify catalog information about an artist's albums.
Args:
artistId (str):
The Spotify ID of the artist.
If null, the currently playing artist uri id value is used.
Example: `6APm8EjxOHSYM5B4i3vT3q`
include_groups (str):
A comma-separated list of keywords that will be used to filter the response.
If not supplied, all album types will be returned.
Valid values are: `album`, `single`, `appears_on`, `compilation`
Example: `single,appears_on`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return; use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that is
available in that market will be returned.
If a valid user access token is specified in the request header, the country associated with
the user account will take priority over this parameter.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `AlbumPageSimplified` object that contains artist album information.
"""
apiMethodName:str = 'service_spotify_get_artist_albums'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("artistId", artistId)
apiMethodParms.AppendKeyValue("include_groups", include_groups)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artist Albums Service", apiMethodParms)
# validations.
if include_groups is None:
include_groups = 'album'
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:AlbumPageSimplified = self.data.spotifyClient.GetArtistAlbums(artistId, include_groups, limit, offset, market, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artist_info(
self,
artistId:str,
) -> dict:
"""
Get artist about information from the Spotify Artist Biography page for the
specified Spotify artist ID.
Args:
artistId (str):
The Spotify ID of the artist.
Example: `6APm8EjxOHSYM5B4i3vT3q`
If null, the currently playing artist uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ArtistInfo` object that contains artist album information.
"""
apiMethodName:str = 'service_spotify_get_artist_info'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("artistId", artistId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artist Info Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:ArtistInfo = self.data.spotifyClient.GetArtistInfo(artistId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artist_related_artists(
self,
artistId:str,
sortResult:bool=True,
) -> dict:
"""
Get Spotify catalog information about artists similar to a given artist.
Similarity is based on analysis of the Spotify community's listening history.
Args:
artistId (str):
The Spotify ID of the artist.
If null, the currently playing artist uri id value is used.
Example: `6APm8EjxOHSYM5B4i3vT3q`
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A list of `Artist` objects that contain artist information.
"""
apiMethodName:str = 'service_spotify_get_artist_related_artists'
apiMethodParms:SIMethodParmListContext = None
result:list = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("artistId", artistId)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artist Related Artists Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:list = self.data.spotifyClient.GetArtistRelatedArtists(artistId, sortResult)
# build dictionary result from array.
resultArray:list = []
item:Artist
for item in result:
resultArray.append(item.ToDictionary())
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": resultArray
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artist_top_tracks(
self,
artistId:str,
market:str=None,
sortResult:bool=True,
) -> dict:
"""
Get Spotify catalog information about an artist's top tracks by country.
Args:
artistId (str):
The Spotify ID of the artist.
Example: `6APm8EjxOHSYM5B4i3vT3q`
If null, the currently playing artist uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A list of `Track` objects of matching results.
"""
apiMethodName:str = 'service_spotify_get_artist_top_tracks'
apiMethodParms:SIMethodParmListContext = None
result:list = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("artistId", artistId)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artist Top Tracks Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:list = self.data.spotifyClient.GetArtistTopTracks(artistId, market, sortResult)
# build dictionary result from array.
resultArray:list = []
item:Track
for item in result:
resultArray.append(item.ToDictionary())
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": resultArray
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_artists_followed(
self,
after:str=None,
limit:int=20,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get the current user's followed artists.
Args:
after (str):
The last artist ID retrieved from the previous request, or null for
the first request.
Example: `0I2XqVXqHScXjHhk6AYYRe`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ArtistPage` object that contains artist information.
"""
apiMethodName:str = 'service_spotify_get_artists_followed'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("after", after)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Artists Followed Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:ArtistPage = self.data.spotifyClient.GetArtistsFollowed(after, limit, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_audiobook(
self,
audiobookId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single audiobook.
Audiobooks are only available within the US, UK, Canada, Ireland, New Zealand and Australia markets.
Args:
audiobookId (str):
The Spotify ID for the audiobook.
Example: `74aydHJKgYz3AIq3jjBSv1`
If null, the currently playing audiobook uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Audiobook` object that contains audiobook details.
"""
apiMethodName:str = 'service_spotify_get_audiobook'
apiMethodParms:SIMethodParmListContext = None
result: Audiobook = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("audiobookId", audiobookId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Audiobook Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:Audiobook = self.data.spotifyClient.GetAudiobook(audiobookId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_audiobook_chapters(
self,
audiobookId:str=None,
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about an audiobook's chapters.
Args:
audiobookId (str):
The Spotify ID for the audiobook.
Example: `74aydHJKgYz3AIq3jjBSv1`
If null, the currently playing audiobook uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
limit (int):
The maximum number of items to return in a page of items when manual paging is used.
Default: 20, Range: 1 to 50.
See the `limitTotal` argument for automatic paging option.
offset (int):
The index of the first item to return; use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `ChapterPageSimplified` object that contains simplified chapter information for the audiobook Id.
"""
apiMethodName:str = 'service_spotify_get_audiobook_chapters'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("audiobookId", audiobookId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Audiobook Chapters Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:AudiobookPageSimplified = self.data.spotifyClient.GetAudiobookChapters(audiobookId, limit, offset, market, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_audiobook_favorites(
self,
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of the audiobooks saved in the current Spotify user's 'Your Library'.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `AudiobookPageSimplified` object that contains saved audiobook information.
"""
apiMethodName:str = 'service_spotify_get_audiobook_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Audiobook Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:AudiobookPageSimplified = self.data.spotifyClient.GetAudiobookFavorites(limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_browse_categorys_list(
self,
country:str=None,
locale:str=None,
refresh:str=False,
) -> dict:
"""
Get a sorted list of ALL categories used to tag items in Spotify.
Args:
country (str):
A country: an ISO 3166-1 alpha-2 country code.
Provide this parameter if you want the list of returned items to be relevant to a
particular country. If omitted, the returned items will be relevant to all countries.
Example: `SE`
locale (str):
The desired language, consisting of a lowercase ISO 639-1 language code and an uppercase
ISO 3166-1 alpha-2 country code, joined by an underscore.
For example: `es_MX`, meaning "Spanish (Mexico)".
Provide this parameter if you want the results returned in a particular language (where available).
Note: if locale is not supplied, or if the specified language is not available, all strings will
be returned in the Spotify default language (American English). The locale parameter, combined with
the country parameter, may give odd results if not carefully matched. For example country=`SE` and
locale=`de_DE` will return a list of categories relevant to Sweden but as German language strings.
Example: `sv_SE`
refresh (bool):
True to return real-time information from the spotify web api and
update the cache; otherwise, False to just return the cached value.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `CategoryPage` object that contains the list category details.
"""
apiMethodName:str = 'service_spotify_get_browse_categorys_list'
apiMethodParms:SIMethodParmListContext = None
result:CategoryPage = CategoryPage()
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("country", country)
apiMethodParms.AppendKeyValue("locale", locale)
apiMethodParms.AppendKeyValue("refresh", refresh)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Browse Categorys List Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
resultArray:list[Category] = self.data.spotifyClient.GetBrowseCategorysList(country, locale, refresh)
# build a CategoryPage object so we can convert to a dictionary.
category:Category
for category in resultArray:
result.Items.append(category)
# set CategoryPage object summary.
result.Total = len(resultArray)
result.Limit = result.Total
result.DateLastRefreshed = datetime.utcnow().timestamp()
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_category_playlists(
self,
categoryId:str=None,
limit:int=20,
offset:int=0,
country:str=None,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of Spotify playlists tagged with a particular category.
Args:
categoryId (str):
The Spotify category ID for the category.
Example: `dinner`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
country (str):
A country: an ISO 3166-1 alpha-2 country code.
Provide this parameter if you want the list of returned items to be relevant to a
particular country. If omitted, the returned items will be relevant to all countries.
Example: `SE`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `PlaylistPageSimplified` object that contains playlist information.
- message: string that describes what was returned (e.g. 'Popular Playlists').
"""
apiMethodName:str = 'service_spotify_get_category_playlists'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("categoryId", categoryId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("country", country)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Category Playlists Service", apiMethodParms)
# request information from Spotify Web API.
# have to treat this one a little bit differently due to return of a Tuple[] value.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
response:Tuple[PlaylistPageSimplified, str]
response = self.data.spotifyClient.GetCategoryPlaylists(categoryId, limit, offset, country, limitTotal, sortResult)
_logsi.LogObject(SILevel.Verbose, "response Tuple object", response)
result:PlaylistPageSimplified = response[0]
message:str = response[1]
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary(),
"message": message
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_chapter(
self,
chapterId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single audiobook chapter identified by its unique Spotify ID.
Args:
chapterId (str):
The Spotify ID of the chapter.
Example: `3V0yw9UDrYAfkhAvTrvt9Y`
If null, the currently playing chapter uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Chapter` object that contain the chapter details.
"""
apiMethodName:str = 'service_spotify_get_chapter'
apiMethodParms:SIMethodParmListContext = None
result:Chapter = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("chapterId", chapterId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Chapter Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetChapter(chapterId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_cover_image_file(
self,
imageUrl:str,
outputPath:str,
) -> None:
"""
Gets the contents of an image url and transfers the contents to the local file system.
Args:
imageUrl (str | list[ImageObject]):
The cover image url whose contents are to be retrieved.
outputPath (str):
Fully-qualified path to store the downloaded image to.
The output path supports the replacement of the following keyword parameters:
- `{dotfileextn}` - a "." followed by the file extension based on response content
type (for known types: JPG,PNG,APNG,BMP,GIF - defaults to JPG).
This method should only be used to download images for playlists that contain
public domain images. It should not be used to download copyright protected images,
as that would violate the Spotify Web API Terms of Service.
"""
apiMethodName:str = 'service_spotify_get_cover_image_file'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("imageUrl", imageUrl)
apiMethodParms.AppendKeyValue("outputPath", outputPath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Cover Image File Service", apiMethodParms)
# get cover image file.
_logsi.LogVerbose("Retrieving cover image file")
self.data.spotifyClient.GetCoverImageFile(imageUrl, outputPath)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_device_playback_state(
self,
deviceId:str=None,
) -> dict:
"""
Get information about the current playback state, including track or episode, and progress.
If the Spotify Web API reports nothing as playing, then the device-specific playback state
is returned (if one exists).
This is not part of the Spotify Web API specification.
Args:
deviceId (str | SpotifyConnectDevice) | None):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), or null to
utilize the active player device.
An exception will be raised if the argument value could not be resolved.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayerPlayState` object that contains the playback state details.
"""
apiMethodName:str = 'service_spotify_get_device_playback_state'
apiMethodParms:SIMethodParmListContext = None
result:PlayerPlayState = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Device Playback State Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetDevicePlaybackState(deviceId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_episode(
self,
episodeId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single episode identified by its unique Spotify ID.
Args:
episodeId (str):
The Spotify ID of the episode.
Example: `512ojhOuo1ktJprKbVcKyQ`
If null, the currently playing episode uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Episode` object that contain the episode details.
"""
apiMethodName:str = 'service_spotify_get_episode'
apiMethodParms:SIMethodParmListContext = None
result:Episode = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("episodeId", episodeId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Episode Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetEpisode(episodeId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_episode_favorites(
self,
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of the episodes saved in the current Spotify user's 'Your Library'.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `EpisodePageSaved` object that contains saved audiobook information.
"""
apiMethodName:str = 'service_spotify_get_episode_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Episode Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:EpisodePageSaved = self.data.spotifyClient.GetEpisodeFavorites(limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_featured_playlists(
self,
limit:int=20,
offset:int=0,
country:str=None,
locale:str=None,
timestamp:str=None,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of Spotify featured playlists (shown, for example, on a Spotify player's 'Browse' tab).
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
country (str):
A country: an ISO 3166-1 alpha-2 country code.
Provide this parameter if you want the list of returned items to be relevant to a
particular country. If omitted, the returned items will be relevant to all countries.
Example: `SE`
locale (str):
The desired language, consisting of a lowercase ISO 639-1 language code and an uppercase
ISO 3166-1 alpha-2 country code, joined by an underscore.
For example: `es_MX`, meaning "Spanish (Mexico)".
Provide this parameter if you want the results returned in a particular language (where available).
Note: if locale is not supplied, or if the specified language is not available, all strings will
be returned in the Spotify default language (American English). The locale parameter, combined with
the country parameter, may give odd results if not carefully matched. For example country=`SE` and
locale=`de_DE` will return a list of categories relevant to Sweden but as German language strings.
Example: `sv_SE`
timestamp (str):
A timestamp in ISO 8601 format: yyyy-MM-ddTHH:mm:ss.
Use this parameter to specify the user's local time to get results tailored for that specific date
and time in the day. If not provided, the response defaults to the current UTC time.
Example: `2023-10-23T09:00:00` for a user whose local time is 9AM.
If there were no featured playlists (or there is no data) at the specified time, the response will
revert to the current UTC time.
Example: `2023-10-23T09:00:00`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `PlaylistPageSimplified` object that contains playlist information.
- message: string that describes what was returned (e.g. 'Popular Playlists').
"""
apiMethodName:str = 'service_spotify_get_featured_playlists'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("country", country)
apiMethodParms.AppendKeyValue("locale", locale)
apiMethodParms.AppendKeyValue("timestamp", timestamp)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Featured Playlists Service", apiMethodParms)
# request information from Spotify Web API.
# have to treat this one a little bit differently due to return of a Tuple[] value.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
response:Tuple[PlaylistPageSimplified, str]
response = self.data.spotifyClient.GetFeaturedPlaylists(limit, offset, country, locale, timestamp, limitTotal, sortResult)
_logsi.LogObject(SILevel.Verbose, "response Tuple object", response)
result:PlaylistPageSimplified = response[0]
message:str = response[1]
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary(),
"message": message
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_id_from_uri(
self,
uri:str=None,
) -> dict:
"""
Gets the Id portion of a Spotify URI value.
Args:
uri (str):
The Spotify URI value.
Example: `spotify:track:6vc9OTcyd3hyzabCmsdnwE`
If omitted, the currently playing uri value is used.
Returns:
A dictionary that contains the following keys:
- result: A string object that contains the id value.
"""
apiMethodName:str = 'service_spotify_get_id_from_uri'
apiMethodParms:SIMethodParmListContext = None
result:Album = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uri", uri)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Id From Uri Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetIdFromUri(uri)
# return the result itself.
return {
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_image_palette_colors(
self,
imageSource:str=None,
colorCount:int=10,
colorQuality:int=3,
brightnessFilterLow:int=None,
brightnessFilterHigh:int=None,
hueDistanceFilter:int=None,
) -> dict:
"""
Extracts color palette RGB values from the specified image source.
Args:
imageSource (str):
The image source to extract color palette information from. If the prefix of the
value is `http:` or `https:`, then the image is downloaded from the url.
This can also point to a filename on the local file system.
If null, the currently playing Spotify track image url is used; if no track
is playing, then an exception is raised.
Example: `http://mydomain/image1.jpg`
Example: `c:/image1.jpg`
Default is None.
colorCount (int):
The size of the palette (max number of colors).
Range is 1 to 256.
Default is 10.
colorQuality (int):
Controls the processing time and quality of the palette generation.
A lower value (e.g. 1) results in higher quality but takes more processing time,
while a higher value (e.g. 5) is faster but may result in a lower-quality palette.
Default is 3.
brightnessFilterLow (int):
Removes colors that are too dark based on their brightness value.
Range is 0 to 765.
Default is None.
brightnessFilterHigh (int):
Remove colors that are too light based on their brightness value.
Range is 0 to 765.
Default is None.
hueDistanceFilter (int):
Remove colors that are too close to each other for the specified hue.
This keeps the colors looking fairly distinct.
Range is 0 to 360.
Default is None.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ImagePaletteColors` object that contains extracted color information.
"""
apiMethodName:str = 'service_spotify_get_image_palette_colors'
apiMethodParms:SIMethodParmListContext = None
result:ImagePaletteColors = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("imageSource", imageSource)
apiMethodParms.AppendKeyValue("colorCount", colorCount)
apiMethodParms.AppendKeyValue("colorQuality", colorQuality)
apiMethodParms.AppendKeyValue("brightnessFilterLow", brightnessFilterLow)
apiMethodParms.AppendKeyValue("brightnessFilterHigh", brightnessFilterHigh)
apiMethodParms.AppendKeyValue("hueDistanceFilter", hueDistanceFilter)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get palette colors from an image url", apiMethodParms)
# request information from Spotify Web API.
# have to treat this one a little bit differently due to return of a Tuple[] value.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetImagePaletteColors(imageSource, colorCount, colorQuality, brightnessFilterLow, brightnessFilterHigh, hueDistanceFilter)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_image_vibrant_colors(
self,
imageSource:str=None,
colorCount:int=64,
colorQuality:int=5,
) -> dict:
"""
Extracts vibrant color palette RGB values from the specified image source.
Args:
imageSource (str):
The image source to extract color palette information from. If the prefix of the
value is `http:` or `https:`, then the image is downloaded from the url.
This can also point to a filename on the local file system.
If null, the currently playing Spotify track image url is used.
Example: `http://mydomain/image1.jpg`
Example: `c:/image1.jpg`
colorCount (int):
The number of colors in the initial palette from which swatches will be generated.
Default is 64.
colorQuality (int):
Controls the processing time and quality of the palette generation.
A lower value (e.g. 1) results in higher quality but takes more processing time,
while a higher value (e.g. 5) is faster but may result in a lower-quality palette.
Default is 5.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ImageVibrantColors` object that contains extracted color information.
"""
apiMethodName:str = 'service_spotify_get_image_vibrant_colors'
apiMethodParms:SIMethodParmListContext = None
result:ImageVibrantColors = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("imageSource", imageSource)
apiMethodParms.AppendKeyValue("colorCount", colorCount)
apiMethodParms.AppendKeyValue("colorQuality", colorQuality)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get vibrant colors from an image url", apiMethodParms)
# request information from Spotify Web API.
# have to treat this one a little bit differently due to return of a Tuple[] value.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetImageVibrantColors(imageSource, colorCount, colorQuality)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_devices(
self,
refresh:bool=True,
sortResult:bool=True,
) -> dict:
"""
Get information about a user's available Spotify Connect player devices.
Some device models are not supported and will not be listed in the API response.
This method requires the `user-read-playback-state` scope.
Args:
refresh (bool):
True (default) to return real-time information from the spotify web api and
update the cache; otherwise, False to just return the cached value.
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A list of `Device` objects that contain the device details, sorted by name.
"""
apiMethodName:str = 'service_spotify_get_player_devices'
apiMethodParms:SIMethodParmListContext = None
result:list = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("refresh", refresh)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Devices Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlayerDevices(refresh, sortResult)
# build dictionary result from array.
resultArray:list = []
item:PlayerDevice
for item in result:
resultArray.append(item.ToDictionary())
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": resultArray
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_last_played_info(
self,
) -> dict:
"""
Get information about the content that was last playing on the Spotify Player,
including context, item (track / episode), progress, and active device.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayerLastPlayedInfo` object that contains the player last played details.
"""
apiMethodName:str = 'service_spotify_get_player_last_played_info'
apiMethodParms:SIMethodParmListContext = None
result:PlayerLastPlayedInfo = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Last Played Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.PlayerLastPlayedInfo
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_now_playing(
self,
market:str=None,
additionalTypes:str=None
) -> dict:
"""
Get the object currently being played on the user's Spotify account.
This method requires the `user-read-currently-playing` scope.
Args:
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
additionalTypes (str):
A comma-separated list of item types that your client supports besides the default track type.
Valid types are: `track` and `episode`.
Specify `episode` to get podcast track information.
Note: This parameter was introduced to allow existing clients to maintain their current behaviour
and might be deprecated in the future. In addition to providing this parameter, make sure that your client
properly handles cases of new types in the future by checking against the type field of each object.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayerPlayState` object that contains the player now playing details.
"""
apiMethodName:str = 'service_spotify_get_player_now_playing'
apiMethodParms:SIMethodParmListContext = None
result:PlayerPlayState = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("additionalTypes", additionalTypes)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Now Playing Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlayerNowPlaying(market, additionalTypes)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_playback_state(
self,
market:str=None,
additionalTypes:str=None
) -> dict:
"""
Get information about the user's current playback state, including track or episode, progress,
and active device.
This method requires the `user-read-playback-state` scope.
Args:
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
additionalTypes (str):
A comma-separated list of item types that your client supports besides the default track type.
Valid types are: `track` and `episode`.
Specify `episode` to get podcast track information.
Note: This parameter was introduced to allow existing clients to maintain their current behaviour
and might be deprecated in the future. In addition to providing this parameter, make sure that your client
properly handles cases of new types in the future by checking against the type field of each object.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayerPlayState` object that contains the playback state details.
"""
apiMethodName:str = 'service_spotify_get_player_playback_state'
apiMethodParms:SIMethodParmListContext = None
result:PlayerPlayState = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("additionalTypes", additionalTypes)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Playback State Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlayerPlaybackState(market, additionalTypes)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_queue_info(self) -> dict:
"""
Get the list of objects that make up the user's playback queue.
This method requires the `user-read-currently-playing` and `user-read-playback-state` scope.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayerQueueInfo` object that contains the player queue information.
"""
apiMethodName:str = 'service_spotify_get_player_queue_info'
apiMethodParms:SIMethodParmListContext = None
result:PlayerQueueInfo = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Queue Info Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlayerQueueInfo()
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError (ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError (ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_player_recent_tracks(
self,
limit:int=20,
after:int=0,
before:int=0,
limitTotal:int=None
) -> dict:
"""
Get tracks from the current user's recently played tracks.
Note: Currently doesn't support podcast episodes.
This method requires the `user-read-recently-played` scope.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: `20`, Range: `1` to `50`.
after (int):
Returns all items after (but not including) this cursor position, which is
a Unix timestamp in milliseconds.
If `after` is specified, `before` must not be specified.
Use with limit to get the next set of items.
Default: `0` (the first item).
before (int):
Returns all items before (but not including) this cursor position, which is
a Unix timestamp in milliseconds.
If `before` is specified, `after` must not be specified.
Use with limit to get the next set of items.
Default: `0` (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
The `after` and `before` arguments are based upon local time (not UTC time). Recently
played item history uses a local timestamp, and NOT a UTC timestamp.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlayHistoryPage` object that contains the playlist details.
"""
apiMethodName:str = 'service_spotify_get_player_recent_tracks'
apiMethodParms:SIMethodParmListContext = None
result:PlayHistoryPage = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("after", after)
apiMethodParms.AppendKeyValue("before", before)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Player Recent Tracks Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlayerRecentTracks(limit, after, before, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_playlist(
self,
playlistId:str=None,
market:str=None,
fields:str=None,
additionalTypes:str=None
) -> dict:
"""
Get a playlist owned by a Spotify user.
Args:
playlistId (str):
The Spotify ID of the playlist.
If null, the currently playing playlist uri id value is used.
Example: `5v5ETK9WFXAnGQ3MRubKuE`
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Example: `ES`
fields (str):
Filters for the query: a comma-separated list of the fields to return.
If omitted, all fields are returned.
additionalTypes (str):
A comma-separated list of item types that your client supports besides the default track type.
Valid types are: track and episode.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Playlist` object that contains the playlist details.
"""
apiMethodName:str = 'service_spotify_get_playlist'
apiMethodParms:SIMethodParmListContext = None
result:Playlist = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("fields", fields)
apiMethodParms.AppendKeyValue("additionalTypes", additionalTypes)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Playlist Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlaylist(playlistId, market, fields, additionalTypes)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_playlist_cover_image(
self,
playlistId:str=None,
) -> dict:
"""
Get the current image associated with a specific playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
If null, the currently playing playlist uri id value is used.
Example: `5v5ETK9WFXAnGQ3MRubKuE`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ImageObject` object that contains the playlist cover image details.
"""
apiMethodName:str = 'service_spotify_get_playlist_cover_image'
apiMethodParms:SIMethodParmListContext = None
result:Playlist = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Playlist Cover Image Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetPlaylistCoverImage(playlistId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_playlist_favorites(
self,
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of the playlists owned or followed by the current Spotify user.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlaylistPageSimplified` object that contains playlist information.
"""
apiMethodName:str = 'service_spotify_get_playlist_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Playlist Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:PlaylistPageSimplified = self.data.spotifyClient.GetPlaylistFavorites(limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_playlist_items(
self,
playlistId:str=None,
limit:int=20,
offset:int=0,
market:str=None,
fields:str=None,
additionalTypes:str=None,
limitTotal:int=None
) -> dict:
"""
Get full details of the items of a playlist owned by a Spotify user.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5v5ETK9WFXAnGQ3MRubKuE`
If null, the currently playing playlist uri id value is used.
limit (int):
The maximum number of items to return in a page of items when manual paging is used.
Default: 50, Range: 1 to 50.
See the `limitTotal` argument for automatic paging option.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
fields (str):
Filters for the query: a comma-separated list of the fields to return.
If omitted, all fields are returned.
For example, to get just the playlist's description and URI:
`fields=description,uri`.
A dot separator can be used to specify non-reoccurring fields, while parentheses can be used
to specify reoccurring fields within objects. For example, to get just the added date and user
ID of the adder:
`fields=tracks.items(added_at,added_by.id)`.
Use multiple parentheses to drill down into nested objects, for example:
`fields=tracks.items(track(name,href,album(name,href)))`.
Fields can be excluded by prefixing them with an exclamation mark, for example:
`fields=tracks.items(track(name,href,album(!name,href)))`
Example: fields=items(added_by.id,track(name,href,album(name,href)))
additionalTypes (str):
A comma-separated list of item types that your client supports besides the default track type.
Valid types are: track and episode.
Note: This parameter was introduced to allow existing clients to maintain their current behaviour
and might be deprecated in the future. In addition to providing this parameter, make sure that your
client properly handles cases of new types in the future by checking against the type field of each object.
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlaylistPage` object that contains playlist information.
"""
apiMethodName:str = 'service_spotify_get_playlist_items'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("fields", fields)
apiMethodParms.AppendKeyValue("additionalTypes", additionalTypes)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Playlist Items Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:PlaylistPage = self.data.spotifyClient.GetPlaylistItems(playlistId, limit, offset, market, fields, additionalTypes, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_playlists_for_user(
self,
userId:str,
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get a list of the playlists owned or followed by the current Spotify user.
Args:
userId (str):
The user's Spotify user ID.
Example: `smedjan`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlaylistPageSimplified` object that contains user playlist information.
"""
apiMethodName:str = 'service_spotify_get_playlists_for_user'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("userId", userId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Playlists For User Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:PlaylistPageSimplified = self.data.spotifyClient.GetPlaylistsForUser(userId, limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_show(
self,
showId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single show identified by its unique Spotify ID.
Args:
showId (str):
The Spotify ID for the show.
If null, the currently playing show uri id value is used.
Example: `5CfCWKI5pZ28U0uOzXkDHe`
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Show` object that contain the show details.
"""
apiMethodName:str = 'service_spotify_get_show'
apiMethodParms:SIMethodParmListContext = None
result:Show = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("showId", showId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Show Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetShow(showId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_show_episodes(
self,
showId:str=None,
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None
) -> dict:
"""
Get Spotify catalog information about a show's episodes.
Args:
showId (str):
The Spotify ID for the show.
If null, the currently playing show uri id value is used.
Example: `6kAsbP8pxwaU2kPibKTuHE`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return; use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `EpisodePageSimplified` object that contains the show episodes.
"""
apiMethodName:str = 'service_spotify_get_show_episodes'
apiMethodParms:SIMethodParmListContext = None
result:EpisodePageSimplified = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("showId", showId)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("additionaoffsetlTypes", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Show Episodes Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetShowEpisodes(showId, limit, offset, market, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_show_favorites(
self,
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
excludeAudiobooks:bool=True,
) -> dict:
"""
Get a list of the shows saved in the current Spotify user's 'Your Library'.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
excludeAudiobooks (bool):
True to exclude audiobook shows from the returned list, leaving only podcast shows;
otherwise, False to include all results returned by the Spotify Web API.
Default: True
For some reason, Spotify Web API returns audiobooks AND podcasts with the `/me/shows` service.
Spotify Web API returns only audiobooks with the `/me/audiobooks` service.
The reasoning for that is unclear, but the `excludeAudiobooks` argument allows you to
only return podcast shows in the results if desired.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `ShowPageSaved` object that contains playlist information.
"""
apiMethodName:str = 'service_spotify_get_show_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
apiMethodParms.AppendKeyValue("excludeAudiobooks", excludeAudiobooks)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Show Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:ShowPageSaved = self.data.spotifyClient.GetShowFavorites(limit, offset, limitTotal, sortResult, excludeAudiobooks)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_spotify_connect_device(
self,
deviceValue:str,
verifyUserContext:bool=True,
verifyTimeout:float=5,
refreshDeviceList:bool=True,
activateDevice:bool=True,
delay:float=0.25,
) -> dict:
"""
Get information about a specific Spotify Connect player device, and (optionally)
activate the device if it requires it.
This method requires the `user-read-playback-state` scope.
Resolves a Spotify Connect device from a specified device id, name, alias id,
or alias name. This will ensure that the device can be found on the network, as well
as connect to the device if necessary with the current user context.
Args:
deviceValue (str):
The device id / name value to check.
verifyUserContext (bool):
If True, the active user context of the resolved device is checked to ensure it
matches the user context specified on the class constructor.
If False, the user context will not be checked.
Default is True.
verifyTimeout (float):
Maximum time to wait (in seconds) for the device to become active in the Spotify
Connect device list. This value is only used if a Connect command has to be
issued to activate the device.
Default is 5; value range is 0 - 10.
refreshDeviceList (bool):
True to refresh the Spotify Connect device list; otherwise, False to use the
Spotify Connect device list cache.
activateDevice (bool):
True to activate the device if necessary; otherwise, False.
delay (float):
Time delay (in seconds) to wait AFTER issuing any command to the device.
This delay will give the spotify zeroconf api time to process the change before
another command is issued.
Default is 0.25; value range is 0 - 10.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `SpotifyConnectDevice` object that contains the device details.
"""
apiMethodName:str = 'service_spotify_get_spotify_connect_device'
apiMethodParms:SIMethodParmListContext = None
result:SpotifyConnectDevice = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceValue", deviceValue)
apiMethodParms.AppendKeyValue("verifyUserContext", verifyUserContext)
apiMethodParms.AppendKeyValue("verifyTimeout", verifyTimeout)
apiMethodParms.AppendKeyValue("refreshDeviceList", refreshDeviceList)
apiMethodParms.AppendKeyValue("activateDevice", activateDevice)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Get Spotify Connect Device service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetSpotifyConnectDevice(deviceValue, verifyUserContext, verifyTimeout, refreshDeviceList, activateDevice, delay)
# if result not found then raise exception.
if result is None:
raise ServiceValidationError('Device "%s" could not be found in the Spotify Connect device list.' % deviceValue)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_spotify_connect_devices(
self,
refresh:bool=True,
sortResult:bool=True,
) -> dict:
"""
Get information about all available Spotify Connect player devices.
This method requires the `user-read-playback-state` scope.
Args:
refresh (bool):
True (default) to return real-time information from the spotify zeroconf api and
update the cache; otherwise, False to just return the cached value.
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `SpotifyConnectDevices` object that contain the device details.
"""
apiMethodName:str = 'service_spotify_get_spotify_connect_devices'
apiMethodParms:SIMethodParmListContext = None
result:SpotifyConnectDevices = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("refresh", refresh)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Spotify Connect Devices Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetSpotifyConnectDevices(refresh, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_track(
self,
trackId:str=None,
market:str=None,
) -> dict:
"""
Get Spotify catalog information for a single track identified by its unique Spotify ID.
Args:
trackId (str):
The Spotify ID of the track.
Example: `1kWUud3vY5ij5r62zxpTRy`
If null, the currently playing track uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Track` object that contain the track details.
"""
apiMethodName:str = 'service_spotify_get_track'
apiMethodParms:SIMethodParmListContext = None
result:Track = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("trackId", trackId)
apiMethodParms.AppendKeyValue("market", market)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Track Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetTrack(trackId, market)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_track_audio_features(
self,
track_id:str,
) -> dict:
"""
Get audio feature information for a single track identified by its unique Spotify ID.
Args:
trackId (str):
The Spotify ID of the track.
Example: `1kWUud3vY5ij5r62zxpTRy`
If null, the currently playing track uri id value is used; a Spotify Free or Premium account
is required to correctly read the currently playing context.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `AudioFeatures` object that contain the audio feature details.
"""
apiMethodName:str = 'service_spotify_get_track_audio_features'
apiMethodParms:SIMethodParmListContext = None
result:AudioFeatures = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("track_id", track_id)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Track Audio Features Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result = self.data.spotifyClient.GetTrackAudioFeatures(track_id)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_track_favorites(
self,
limit:int=20,
offset:int=0,
market:str=None,
limitTotal:int=None,
sortResult:bool=True,
filterArtist:str=None,
filterAlbum:str=None,
) -> dict:
"""
Get a list of the tracks saved in the current Spotify user's 'Your Library'.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item).
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Example: `ES`
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
filterArtist (str):
Filter returned entries by an artist name.
Value can be the full name of the artist (e.g. "Jeremy Camp"), or a partial name (e.g. "Camp").
filterAlbum (str):
Filter returned entries by an album name.
Value can be the full name of the album (e.g. "Carried Me"), or a partial name (e.g. "Carried").
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `TrackPageSaved` object that contains track favorites.
"""
apiMethodName:str = 'service_spotify_get_track_favorites'
apiMethodParms:SIMethodParmListContext = None
result:TrackPageSaved = TrackPageSaved()
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
apiMethodParms.AppendKeyValue("filterArtist", filterArtist)
apiMethodParms.AppendKeyValue("filterAlbum", filterAlbum)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Track Favorites Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:TrackPageSaved = self.data.spotifyClient.GetTrackFavorites(limit, offset, market, limitTotal, sortResult, filterArtist, filterAlbum)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_track_recommendations(
self,
limit:int=20,
market:str=None,
seedArtists:str=None,
seedGenres:str=None,
seedTracks:str=None,
minAcousticness:float=None, maxAcousticness:float=None, targetAcousticness:float=None,
minDanceability:float=None, maxDanceability:float=None, targetDanceability:float=None,
minDurationMS:int=None, maxDurationMS:int=None, targetDurationMS:int=None,
minEnergy:float=None, maxEnergy:float=None, targetEnergy:float=None,
minInstrumentalness:float=None, maxInstrumentalness:float=None, targetInstrumentalness:float=None,
minKey:int=None, maxKey:int=None, targetKey:int=None,
minLiveness:float=None, maxLiveness:float=None, targetLiveness:float=None,
minLoudness:float=None, maxLoudness:float=None, targetLoudness:float=None,
minMode:float=None, maxMode:float=None, targetMode:float=None,
minPopularity:int=None, maxPopularity:int=None, targetPopularity:int=None,
minSpeechiness:float=None, maxSpeechiness:float=None, targetSpeechiness:float=None,
minTempo:int=None, maxTempo:int=None, targetTempo:int=None,
minTimeSignature:int=None, maxTimeSignature:int=None, targetTimeSignature:int=None,
minValence:float=None, maxValence:float=None, targetValence:float=None
) -> dict:
"""
Get track recommendations for specified criteria.
Args:
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
seedArtists (str):
A comma separated list of Spotify IDs for seed artists.
Up to 5 seed values may be provided in any combination of seedArtists, seedTracks and seedGenres.
Note: only required if seedGenres and seedTracks are not set.
Example: `4NHQUGzhtTLFvgF5SZesLK`
seedGenres (str):
A comma separated list of any genres in the set of available genre seeds.
Up to 5 seed values may be provided in any combination of seedArtists, seedTracks and seedGenres.
Note: only required if seedArtists and seedTracks are not set.
Example: `classical,country`
seedTracks (str):
A comma separated list of Spotify IDs for a seed track.
Up to 5 seed values may be provided in any combination of seedArtists, seedTracks and seedGenres.
Note: only required if seedArtists and seedGenres are not set.
Example: `0c6xIDDpzE81m2q797ordA`
minAcousticness (float):
Restrict results to only those tracks whose acousticness level is greater than the specified value.
Range: `0` - `1`
maxAcousticness (float):
Restrict results to only those tracks whose acousticness level is less than the specified value.
Range: `0` - `1`
targetAcousticness (float):
Restrict results to only those tracks whose acousticness level is equal to the specified value.
Range: `0` - `1`
minDanceability (float):
Restrict results to only those tracks whose danceability level is greater than the specified value.
Range: `0` - `1`
maxDanceability (float):
Restrict results to only those tracks whose danceability level is less than the specified value.
Range: `0` - `1`
targetDanceability (float):
Restrict results to only those tracks whose acousticness is equal to the specified value.
Range: `0` - `1`
minDurationMS (int):
Restrict results to only those tracks whose duration is greater than the specified value in milliseconds.
maxDurationMS (int):
Restrict results to only those tracks whose duration is less than the specified value in milliseconds.
targetDurationMS (int):
Restrict results to only those tracks whose duration is equal to the specified value in milliseconds.
minEnergy (float):
Restrict results to only those tracks whose energy level is greater than the specified value.
Range: `0` - `1`
maxEnergy (float):
Restrict results to only those tracks whose energy level is less than the specified value.
Range: `0` - `1`
targetEnergy (float):
Restrict results to only those tracks whose energy level is equal to the specified value.
Range: `0` - `1`
minInstrumentalness (float):
Restrict results to only those tracks whose instrumentalness level is greater than the specified value.
Range: `0` - `1`
maxInstrumentalness (float):
Restrict results to only those tracks whose instrumentalness level is less than the specified value.
Range: `0` - `1`
targetInstrumentalness (float):
Restrict results to only those tracks whose instrumentalness level is equal to the specified value.
Range: `0` - `1`
minKey (int):
Restrict results to only those tracks whose key level is greater than the specified value.
Range: `0` - `11`
maxKey (int):
Restrict results to only those tracks whose key level is less than the specified value.
Range: `0` - `11`
targetKey (int):
Restrict results to only those tracks whose key level is equal to the specified value.
Range: `0` - `11`
minLiveness (float):
Restrict results to only those tracks whose liveness level is greater than the specified value.
Range: `0` - `1`
maxLiveness (float):
Restrict results to only those tracks whose liveness level is less than the specified value.
Range: `0` - `1`
targetLiveness (float):
Restrict results to only those tracks whose liveness level is equal to the specified value.
Range: `0` - `1`
minLoudness (float):
Restrict results to only those tracks whose loudness level is greater than the specified value.
maxLoudness (float):
Restrict results to only those tracks whose loudness level is less than the specified value.
targetLoudness (float):
Restrict results to only those tracks whose loudness level is equal to the specified value.
minMode (float):
Restrict results to only those tracks whose mode level is greater than the specified value.
Range: `0` - `1`
maxMode (float):
Restrict results to only those tracks whose mode level is less than the specified value.
Range: `0` - `1`
targetMode (float):
Restrict results to only those tracks whose mode level is equal to the specified value.
Range: `0` - `1`
minPopularity (int):
Restrict results to only those tracks whose popularity level is greater than the specified value.
Range: `0` - `100`
maxPopularity (int):
Restrict results to only those tracks whose popularity level is less than the specified value.
Range: `0` - `100`
targetPopularity (int):
Restrict results to only those tracks whose popularity level is equal to the specified value.
Range: `0` - `100`
minSpeechiness (float):
Restrict results to only those tracks whose speechiness level is greater than the specified value.
Range: `0` - `1`
maxSpeechiness (float):
Restrict results to only those tracks whose speechiness level is less than the specified value.
Range: `0` - `1`
targetSpeechiness (float):
Restrict results to only those tracks whose speechiness level is equal to the specified value.
Range: `0` - `1`
minTempo (int):
Restrict results to only those tracks with a tempo greater than the specified number of beats per minute.
maxTempo (int):
Restrict results to only those tracks with a tempo less than the specified number of beats per minute.
targetTempo (int):
Restrict results to only those tracks with a tempo equal to the specified number of beats per minute.
minTimeSignature (int):
Restrict results to only those tracks whose time signature is greater than the specified value.
Maximum value: 11
maxTimeSignature (int):
Restrict results to only those tracks whose time signature is less than the specified value.
targetTimeSignature (int):
Restrict results to only those tracks whose time signature is equal to the specified value.
minValence (float):
Restrict results to only those tracks whose valence level is greater than the specified value.
Range: `0` - `1`
maxValence (float):
Restrict results to only those tracks whose valence level is less than the specified value.
Range: `0` - `1`
targetValence (float):
Restrict results to only those tracks whose valence level is equal to the specified value.
Range: `0` - `1`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `TrackRecommendations` object that contains track information.
"""
apiMethodName:str = 'service_spotify_get_track_recommendations'
apiMethodParms:SIMethodParmListContext = None
result:TrackRecommendations = TrackRecommendations()
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("seedArtists", seedArtists)
apiMethodParms.AppendKeyValue("seedGenres", seedGenres)
apiMethodParms.AppendKeyValue("seedTracks", seedTracks)
apiMethodParms.AppendKeyValue("minAcousticness", minAcousticness)
apiMethodParms.AppendKeyValue("maxAcousticness", maxAcousticness)
apiMethodParms.AppendKeyValue("targetAcousticness", targetAcousticness)
apiMethodParms.AppendKeyValue("minDanceability", minDanceability)
apiMethodParms.AppendKeyValue("maxDanceability", maxDanceability)
apiMethodParms.AppendKeyValue("targetDanceability", targetDanceability)
apiMethodParms.AppendKeyValue("minDurationMS", minDurationMS)
apiMethodParms.AppendKeyValue("maxDurationMS", maxDurationMS)
apiMethodParms.AppendKeyValue("targetDurationMS", targetDurationMS)
apiMethodParms.AppendKeyValue("minEnergy", minEnergy)
apiMethodParms.AppendKeyValue("maxEnergy", maxEnergy)
apiMethodParms.AppendKeyValue("targetEnergy", targetEnergy)
apiMethodParms.AppendKeyValue("minInstrumentalness", minInstrumentalness)
apiMethodParms.AppendKeyValue("maxInstrumentalness", maxInstrumentalness)
apiMethodParms.AppendKeyValue("targetInstrumentalness", targetInstrumentalness)
apiMethodParms.AppendKeyValue("minKey", minKey)
apiMethodParms.AppendKeyValue("maxKey", maxKey)
apiMethodParms.AppendKeyValue("targetKey", targetKey)
apiMethodParms.AppendKeyValue("minLiveness", minLiveness)
apiMethodParms.AppendKeyValue("maxLiveness", maxLiveness)
apiMethodParms.AppendKeyValue("targetLiveness", targetLiveness)
apiMethodParms.AppendKeyValue("minLoudness", minLoudness)
apiMethodParms.AppendKeyValue("maxLoudness", maxLoudness)
apiMethodParms.AppendKeyValue("targetLoudness", targetLoudness)
apiMethodParms.AppendKeyValue("minMode", minMode)
apiMethodParms.AppendKeyValue("maxMode", maxMode)
apiMethodParms.AppendKeyValue("targetMode", targetMode)
apiMethodParms.AppendKeyValue("minPopularity", minPopularity)
apiMethodParms.AppendKeyValue("maxPopularity", maxPopularity)
apiMethodParms.AppendKeyValue("targetPopularity", targetPopularity)
apiMethodParms.AppendKeyValue("minSpeechiness", minSpeechiness)
apiMethodParms.AppendKeyValue("maxSpeechiness", maxSpeechiness)
apiMethodParms.AppendKeyValue("targetSpeechiness", targetSpeechiness)
apiMethodParms.AppendKeyValue("minTempo", minTempo)
apiMethodParms.AppendKeyValue("maxTempo", maxTempo)
apiMethodParms.AppendKeyValue("targetTempo", targetTempo)
apiMethodParms.AppendKeyValue("minTimeSignature", minTimeSignature)
apiMethodParms.AppendKeyValue("maxTimeSignature", maxTimeSignature)
apiMethodParms.AppendKeyValue("targetTimeSignature", targetTimeSignature)
apiMethodParms.AppendKeyValue("minValence", minValence)
apiMethodParms.AppendKeyValue("maxValence", maxValence)
apiMethodParms.AppendKeyValue("targetValence", targetValence)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Track Recommendations Service", apiMethodParms)
# reset default values to None so they are processed correctly by the api.
if minAcousticness == 0:
minAcousticness = None
if maxAcousticness == 0:
maxAcousticness = None
if targetAcousticness == 0:
targetAcousticness = None
if minDanceability == 0:
minDanceability = None
if maxDanceability == 0:
maxDanceability = None
if targetDanceability == 0:
targetDanceability = None
if minDurationMS == 0:
minDurationMS = None
if maxDurationMS == 0:
maxDurationMS = None
if targetDurationMS == 0:
targetDurationMS = None
if minEnergy == 0:
minEnergy = None
if maxEnergy == 0:
maxEnergy = None
if targetEnergy == 0:
targetEnergy = None
if minInstrumentalness == 0:
minInstrumentalness = None
if maxInstrumentalness == 0:
maxInstrumentalness = None
if targetInstrumentalness == 0:
targetInstrumentalness = None
if minKey == 0:
minKey = None
if maxKey == 0:
maxKey = None
if targetKey == 0:
targetKey = None
if minLiveness == 0:
minLiveness = None
if maxLiveness == 0:
maxLiveness = None
if targetLiveness == 0:
targetLiveness = None
if minLoudness == 0:
minLoudness = None
if maxLoudness == 0:
maxLoudness = None
if targetLoudness == 0:
targetLoudness = None
if minMode == 0:
minMode = None
if maxMode == 0:
maxMode = None
if targetMode == 0:
targetMode = None
if minPopularity == 0:
minPopularity = None
if maxPopularity == 0:
maxPopularity = None
if targetPopularity == 0:
targetPopularity = None
if minSpeechiness == 0:
minSpeechiness = None
if maxSpeechiness == 0:
maxSpeechiness = None
if targetSpeechiness == 0:
targetSpeechiness = None
if minTempo == 0:
minTempo = None
if maxTempo == 0:
maxTempo = None
if targetTempo == 0:
targetTempo = None
if minTimeSignature == 0:
minTimeSignature = None
if maxTimeSignature == 0:
maxTimeSignature = None
if targetTimeSignature == 0:
targetTimeSignature = None
if minValence == 0:
minValence = None
if maxValence == 0:
maxValence = None
if targetValence == 0:
targetValence = None
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:TrackPageSaved = self.data.spotifyClient.GetTrackRecommendations(
limit, market,
seedArtists, seedGenres, seedTracks,
minAcousticness, maxAcousticness, targetAcousticness,
minDanceability, maxDanceability, targetDanceability,
minDurationMS, maxDurationMS, targetDurationMS,
minEnergy, maxEnergy, targetEnergy,
minInstrumentalness, maxInstrumentalness, targetInstrumentalness,
minKey, maxKey, targetKey,
minLiveness, maxLiveness, targetLiveness,
minLoudness, maxLoudness, targetLoudness,
minMode, maxMode, targetMode,
minPopularity, maxPopularity, targetPopularity,
minSpeechiness, maxSpeechiness, targetSpeechiness,
minTempo, maxTempo, targetTempo,
minTimeSignature, maxTimeSignature, targetTimeSignature,
minValence, maxValence, targetValence)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_tracks_audio_features(
self,
ids:str,
) -> dict:
"""
Get audio features for multiple tracks based on their Spotify IDs.
Args:
ids (list[str]):
A comma-separated list of the Spotify track IDs.
Maximum: 100 IDs.
Example: `7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ,2takcwOaAZWiXQijPHIx7B`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A list of `AudioFeatures` objects that contain the audio feature details.
"""
apiMethodName:str = 'service_spotify_get_tracks_audio_features'
apiMethodParms:SIMethodParmListContext = None
result:list = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Tracks Audio Features Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:TrackPageSaved = self.data.spotifyClient.GetTracksAudioFeatures(ids)
# build dictionary result from array.
resultArray:list = []
item:AudioFeatures
for item in result:
resultArray.append(item.ToDictionary())
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": resultArray
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_users_top_artists(
self,
timeRange:str='medium_term',
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get the current user's top artists based on calculated affinity.
Args:
timeRange (str):
Over what time frame the affinities are computed.
Valid values:
- long_term (calculated from several years of data and including all new data as it becomes available).
- medium_term (approximately last 6 months).
- short_term (approximately last 4 weeks).
Default: `medium_term`
Example: `long_term`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item), Range: 0 to 1000
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ArtistPage` object of matching results.
"""
apiMethodName:str = 'service_spotify_get_users_top_artists'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("timeRange", timeRange)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Users Top Artists Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:ArtistPage = self.data.spotifyClient.GetUsersTopArtists(timeRange, limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_get_users_top_tracks(
self,
timeRange:str='medium_term',
limit:int=20,
offset:int=0,
limitTotal:int=None,
sortResult:bool=True,
) -> dict:
"""
Get the current user's top tracks based on calculated affinity.
Args:
timeRange (str):
Over what time frame the affinities are computed.
Valid values:
- long_term (calculated from several years of data and including all new data as it becomes available).
- medium_term (approximately last 6 months).
- short_term (approximately last 4 weeks).
Default: `medium_term`
Example: `long_term`
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The index of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item), Range: 0 to 1000
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum count specified.
Default: None (disabled)
sortResult (bool):
True to sort the items by name; otherwise, False to leave the items in the same order they
were returned in by the Spotify Web API.
Default: True
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `TrackPage` object of matching results.
"""
apiMethodName:str = 'service_spotify_get_users_top_tracks'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("timeRange", timeRange)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("sortResult", sortResult)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Get Users Top Tracks Service", apiMethodParms)
# request information from Spotify Web API.
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_QUERY_WEB_API)
result:TrackPage = self.data.spotifyClient.GetUsersTopTracks(timeRange, limit, offset, limitTotal, sortResult)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_pause(
self,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Pause media play for the specified Spotify Connect device.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_media_pause'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Pause Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# pause the player.
self.data.spotifyClient.PlayerMediaPause(deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_play_context(
self,
contextUri:str,
offsetUri:str=None,
offsetPosition:int=-1,
positionMS:int=-1,
deviceId:str=None,
delay:float=0.50,
shuffle:bool=None,
playShowLatestEpisode:bool=None,
) -> None:
"""
Start playing one or more tracks of the specified context on a Spotify Connect device.
Args:
contextUri (str):
Spotify URI of the context to play.
Valid contexts are albums, artists & playlists.
Example: `spotify:album:6vc9OTcyd3hyzabCmsdnwE`.
offsetUri (str):
Indicates from what Uri in the context playback should start.
Only available when contextUri corresponds to an artist, album or playlist.
The offsetPosition argument will be used if this value is null.
For Sonos devices, this argument is ignored.
Default is null.
Example: `spotify:track:1301WleyT98MSxVHPZCA6M` start playing at the specified track Uri.
offsetPosition (int):
Indicates from what position in the context playback should start.
The value is zero-based, and must be a positive number, or -1 to disable positioning.
Only available when contextUri corresponds to an album or playlist.
Default is `0`.
Example: `3` start playing at track number 4.
positionMS (int):
The position in milliseconds to seek to; must be a positive number, or -1 to disable positioning.
Passing in a position that is greater than the length of the track will cause the
player to start playing the next track.
Default is `0`.
Example: `25000`
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
shuffle (bool):
True to enable player shuffle mode; False to disable player shuffle mode;
None to use current player shuffle mode.
Default is None.
playShowLatestEpisode (bool):
True to play the most current episode of a podcast show, starting at the beginning;
otherwise, False to resume playing of the podcast episode that was previously played.
Default is False.
This argument is only considered for `show` contexts.
"""
apiMethodName:str = 'service_spotify_player_media_play_context'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("contextUri", contextUri)
apiMethodParms.AppendKeyValue("offsetUri", offsetUri)
apiMethodParms.AppendKeyValue("offsetPosition", offsetPosition)
apiMethodParms.AppendKeyValue("positionMS", positionMS)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
apiMethodParms.AppendKeyValue("shuffle", shuffle)
apiMethodParms.AppendKeyValue("playShowLatestEpisode", playShowLatestEpisode)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Context Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# play one or more items of the specified context on a Spotify Connect device.
self.data.spotifyClient.PlayerMediaPlayContext(contextUri, offsetUri, offsetPosition, positionMS, deviceId, delay, None, shuffle, playShowLatestEpisode)
# check if we need to automatically power on the player.
self._AutoPowerOnCheck()
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_play_track_favorites(
self,
deviceId:str=None,
shuffle:bool=None,
delay:float=0.50,
resolveDeviceId:bool=True,
limitTotal:int=None,
filterArtist:str=None,
filterAlbum:str=None,
) -> None:
"""
Start playing one or more tracks on the specified Spotify Connect device.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
shuffle (bool):
True to set player shuffle mode to on; False to set player shuffle mode to off;
null to leave the shuffle mode as-is.
Default is null.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
resolveDeviceId (bool):
True to resolve the supplied `deviceId` value; otherwise, False not resolve the `deviceId`
value as it has already been resolved.
Default is True.
limitTotal (int):
The maximum number of items to retrieve from favorites.
Default: 200.
filterArtist (str):
Filter returned entries by an artist name.
Value can be the full name of the artist (e.g. "Jeremy Camp"), or a partial name (e.g. "Camp").
filterAlbum (str):
Filter returned entries by an album name.
Value can be the full name of the album (e.g. "Carried Me"), or a partial name (e.g. "Carried").
"""
apiMethodName:str = 'service_spotify_player_media_play_track_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("shuffle", shuffle)
apiMethodParms.AppendKeyValue("delay", delay)
apiMethodParms.AppendKeyValue("resolveDeviceId", resolveDeviceId)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
apiMethodParms.AppendKeyValue("filterArtist", filterArtist)
apiMethodParms.AppendKeyValue("filterAlbum", filterAlbum)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Favorite Tracks Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# play track favorites on the specified Spotify Connect device.
self.data.spotifyClient.PlayerMediaPlayTrackFavorites(deviceId, shuffle, delay, resolveDeviceId, limitTotal, filterArtist, filterAlbum)
# check if we need to automatically power on the player.
self._AutoPowerOnCheck()
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_play_tracks(
self,
uris:str,
positionMS:int=-1,
deviceId:str=None,
delay:float=0.50,
shuffle:bool=None,
) -> None:
"""
Start playing one or more tracks on the specified Spotify Connect device.
Args:
uris (str):
A list of Spotify track URIs to play; can be track or episode URIs.
Example: [`spotify:track:4iV5W9uYEdYUVa79Axb7Rh` ,`spotify:episode:512ojhOuo1ktJprKbVcKyQ`].
It can also be specified as a comma-delimited string.
Example: `spotify:track:4iV5W9uYEdYUVa79Axb7Rh,spotify:episode:512ojhOuo1ktJprKbVcKyQ`.
A maximum of 50 items can be added in one request.
positionMS (int):
The position in milliseconds to seek to; must be a positive number, or -1 to disable positioning.
Passing in a position that is greater than the length of the track will cause the
player to start playing the next track.
Default is `0`.
Example: `25000`
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
shuffle (bool):
True to enable player shuffle mode; False to disable player shuffle mode;
None to use current player shuffle mode.
Default is None.
"""
apiMethodName:str = 'service_spotify_player_media_play_tracks'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uris", uris)
apiMethodParms.AppendKeyValue("positionMS", positionMS)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
apiMethodParms.AppendKeyValue("shuffle", shuffle)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Play Tracks Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# play one or more tracks on the specified Spotify Connect device.
self.data.spotifyClient.PlayerMediaPlayTracks(uris, positionMS, deviceId, delay, None, shuffle)
# check if we need to automatically power on the player.
self._AutoPowerOnCheck()
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_resume(
self,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Resume media play for the specified Spotify Connect device.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_media_resume'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Resume Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# resume the player.
self.data.spotifyClient.PlayerMediaResume(deviceId, delay)
# check if we need to automatically power on the player.
self._AutoPowerOnCheck()
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_seek(
self,
positionMS:int=-1,
deviceId:str=None,
delay:float=0.50,
relativePositionMS:int=0,
) -> None:
"""
Seeks to the given absolute or relative position in the user's currently playing track
for the specified Spotify Connect device.
Args:
positionMS (int):
The absolute position in milliseconds to seek to; must be a positive number.
Passing in a position that is greater than the length of the track will cause the
player to start playing the next song.
Example = `25000` to start playing at the 25 second mark.
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
relativePositionMS (int):
The relative position in milliseconds to seek to; can be a positive or negative number.
Example = `-10000` to seek behind by 10 seconds.
Example = `10000` to seek ahead by 10 seconds.
"""
apiMethodName:str = 'service_spotify_player_media_seek'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("positionMS", positionMS)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
apiMethodParms.AppendKeyValue("relativePositionMS", positionMS)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Seek Service", apiMethodParms)
# validations.
if positionMS == -1:
positionMS = None
if relativePositionMS == 0:
relativePositionMS = None
# validations.
delay = validateDelay(delay, 0.50, 10)
# set seek position.
self.data.spotifyClient.PlayerMediaSeek(positionMS, deviceId, delay, relativePositionMS)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_skip_next(
self,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Skips to next track in the user's queue for the specified Spotify Connect device.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_media_skip_next'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Skip Next Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# skip to next track.
self.data.spotifyClient.PlayerMediaSkipNext(deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
@spotify_exception_handler
def service_spotify_player_media_skip_previous(
self,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Skips to previous track in the user's queue for the specified Spotify Connect device.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_media_skip_previous'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Media Skip Previous Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# skip to previous track.
self.data.spotifyClient.PlayerMediaSkipPrevious(deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_player_set_repeat_mode(
self,
state:str='off',
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set repeat mode for the specified Spotify Connect device.
Args:
state (str):
The repeat mode to set:
- `track` - will repeat the current track.
- `context` - will repeat the current context.
- `off` - will turn repeat off.
Default: `off`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_repeat_mode'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("state", state)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Repeat Mode Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
if state is None:
state = 'off'
# set repeat mode.
self.data.spotifyClient.PlayerSetRepeatMode(state, deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_player_set_shuffle_mode(
self,
state:bool=False,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set shuffle mode for the specified Spotify Connect device.
Args:
state (bool):
The shuffle mode to set:
- `True` - Shuffle user's playback.
- `False` - Do not shuffle user's playback.
Default: `False`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_shuffle_mode'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("state", state)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Shuffle Mode Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
if state is None:
state = False
# set shuffle mode.
self.data.spotifyClient.PlayerSetShuffleMode(state, deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_player_set_volume_level(
self,
volumeLevel:int,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set volume level for the specified Spotify Connect device.
Args:
volumeLevel (int):
The volume to set.
Must be a value from 0 to 100 inclusive.
Example: `50`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_volume_level'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("volumeLevel", volumeLevel)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Volume Level Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
# set volume level.
self.data.spotifyClient.PlayerSetVolume(volumeLevel, deviceId, delay)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a media player command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_player_transfer_playback(
self,
deviceId:str=None,
play:bool=True,
delay:float=0.50,
refreshDeviceList:bool=True,
forceActivateDevice:bool=True,
deviceIdFrom:str=None,
) -> None:
"""
Transfer playback to a new Spotify Connect device and optionally begin playback.
Args:
deviceId (str):
The target player device identifier.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null to utilize the active player device.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument. An exception will be raised if the argument value could
not be resolved or activated.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
play (bool):
The transfer method:
- `True` - ensure playback happens on new device.
- `False` - keep the current playback state.
Default is `True`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
refreshDeviceList (bool):
True to refresh the Spotify Connect device list; otherwise, False to use the
Spotify Connect device list cache.
Default is True.
forceActivateDevice (bool):
True to issue a Spotify Connect Disconnect call prior to transfer, which will
force the device to reconnect to Spotify Connect; otherwise, False to not
disconnect.
Default is True.
deviceIdFrom (str | SpotifyConnectDevice) | None):
The player device identifier where play is being transferred from.
This could be an id, name, a default device indicator (e.g. "*"), a
SpotifyConnectDevice object, or null.
A device is considered resolved if a SpotifyConnectDevice object is passed
for this argument.
Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
The `deviceIdFrom` argument is not part of the Spotify Web API specification. If a value
was supplied, then we will attempt to stop play on the device if it's needed. This is required
if transferring playback from a Sonos device that is playing content on its local queue; if you
don't stop playback on it, then it and the target device will both be playing!
"""
apiMethodName:str = 'service_spotify_player_transfer_playback'
apiMethodParms:SIMethodParmListContext = None
saveSource:str = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("play", play)
apiMethodParms.AppendKeyValue("delay", delay)
apiMethodParms.AppendKeyValue("refreshDeviceList", refreshDeviceList)
apiMethodParms.AppendKeyValue("forceActivateDevice", forceActivateDevice)
apiMethodParms.AppendKeyValue("deviceIdFrom", deviceIdFrom)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Transfer Playback Service", apiMethodParms)
# validations.
delay = validateDelay(delay, 0.50, 10)
if play is None:
play = True
# save from source in the event of an exception.
saveSource = deviceIdFrom
# trace.
_logsi.LogVerbose("'%s': Transferring playback from device \"%s\" to device \"%s\"" % (self.name, deviceIdFrom, deviceId))
_logsi.LogObject(SILevel.Verbose, "'%s': Last known media content: %s" % (self.name, self.data.spotifyClient.PlayerLastPlayedInfo.Summary), self.data.spotifyClient.PlayerLastPlayedInfo, excludeNonPublic=True)
# transfer playback to the specified device.
scDevice:SpotifyConnectDevice = self.data.spotifyClient.PlayerTransferPlayback(
deviceId,
play,
delay,
refreshDeviceList=False,
deviceIdFrom=deviceIdFrom)
# get current Spotify Connect player state (from cache - updated by PlayerTransferPlayback).
self._playerState = self.data.spotifyClient.GetPlayerPlaybackState(refresh=False)
# trace.
if (_logsi.IsOn(SILevel.Debug)):
dictText:str = pformat(self._playerState or "* No State *",indent=2,width=132,sort_dicts=False)
_logsi.LogVerbose("'%s': Spotify Player Playback State AFTER transfer: %s" % (self.name, dictText))
if (self._playerState):
dictText:str = pformat(self._playerState.Device or "* No Device *",indent=2,width=132,sort_dicts=False)
_logsi.LogVerbose("'%s': Spotify Player Playback State AFTER transfer: %s" % (self.name, dictText))
dictText:str = pformat(self._playerState.Item or "* No Track *",indent=2,width=132,sort_dicts=False)
_logsi.LogVerbose("'%s': Spotify Player Playback State AFTER transfer: %s" % (self.name, dictText))
dictText:str = pformat(self._playerState.Context or "* No Context *",indent=2,width=132,sort_dicts=False)
_logsi.LogVerbose("'%s': Spotify Player Playback State AFTER transfer: %s" % (self.name, dictText))
# update the selected source and device instance.
self._spotifyConnectDevice = scDevice
self._attr_source = scDevice.Name
_logsi.LogVerbose("'%s': Selected source was changed to: \"%s\"" % (self.name, scDevice.Title))
# media player command was processed, so force a scan window at the next interval.
_logsi.LogVerbose("'%s': Processed a transfer playback command - forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
except SpotifyApiError as ex:
# assume source could not be transferred if an exception occurred.
self._attr_source = saveSource
_logsi.LogException("'%s': %s; resetting source to: \"%s\"" % (self.name, ex.Message, self._attr_source), ex, logToSystemLogger=False)
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
# assume source could not be transferred if an exception occurred.
self._attr_source = saveSource
_logsi.LogException("'%s': %s; resetting source to: \"%s\"" % (self.name, ex.Message, self._attr_source), ex, logToSystemLogger=False)
raise ServiceValidationError(ex.Message)
except Exception as ex:
# assume source could not be transferred if an exception occurred.
self._attr_source = saveSource
_logsi.LogException("'%s': %s; resetting source to: \"%s\"" % (self.name, ex.Message, self._attr_source), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_cover_image_add(
self,
playlistId:str,
imagePath:str,
) -> None:
"""
Replace the image used to represent a specific playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5v5ETK9WFXAnGQ3MRubKuE`
imagePath (str):
The fully-qualified path of the image to be uploaded.
The image must be in JPEG format, and cannot exceed 256KB in Base64 encoded size.
"""
apiMethodName:str = 'service_spotify_playlist_cover_image_add'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("imagePath", imagePath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Add Cover Image Service", apiMethodParms)
# add playlist cover image.
_logsi.LogVerbose("Adding cover image to Spotify playlist")
self.data.spotifyClient.AddPlaylistCoverImage(playlistId, imagePath)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_change(
self,
playlistId:str,
name:str=None,
description:str=None,
public:bool=True,
collaborative:bool=False,
imagePath:str=None
) -> None:
"""
Change a playlist's details (name, description, and public / private state).
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
name (str):
The updated name for the playlist, for example "My New Playlist Title"
This name does not need to be unique; a user may have several playlists with
the same name.
description (str):
The updated playlist description, as displayed in Spotify Clients and in the Web API.
public (bool):
If true, the playlist will be public; if false, it will be private.
collaborative (bool):
If true, the playlist will become collaborative and other users will be able to modify
the playlist in their Spotify client.
Note: You can only set collaborative to true on non-public playlists.
imagePath (str):
A fully-qualified path of an image to display for the playlist.
The image must be in JPEG format, and cannot exceed 256KB in Base64 encoded size.
Specify null if you do not wish to update the existing playlist image.
"""
apiMethodName:str = 'service_spotify_playlist_change'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("name", name)
apiMethodParms.AppendKeyValue("description", description)
apiMethodParms.AppendKeyValue("public", public)
apiMethodParms.AppendKeyValue("collaborative", collaborative)
apiMethodParms.AppendKeyValue("imagePath", imagePath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Change Service", apiMethodParms)
# create Spotify playlist.
_logsi.LogVerbose("Changing Spotify Playlist Details")
self.data.spotifyClient.ChangePlaylistDetails(playlistId, name, description, public, collaborative, imagePath)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_create(
self,
userId:str,
name:str=None,
description:str=None,
public:bool=True,
collaborative:bool=False,
imagePath:str=None
) -> dict:
"""
Create an empty playlist for a Spotify user.
This method requires the `playlist-modify-public` and `playlist-modify-private` scope.
Args:
userId (str):
The user's Spotify user ID.
Example: `32k99y2kg5lnn3mxhtmd2bpdkjfu`
name (str):
The name for the new playlist, for example "My Playlist".
This name does not need to be unique; a user may have several playlists with
the same name.
description (str):
The playlist description, as displayed in Spotify Clients and in the Web API.
public (bool):
If true, the playlist will be public; if false, it will be private.
To be able to create private playlists, the user must have granted the
`playlist-modify-private` scope.
Defaults to true.
collaborative (bool):
If true, the playlist will be collaborative.
Note: to create a collaborative playlist you must also set public to false.
To create collaborative playlists you must have granted `playlist-modify-private`
and `playlist-modify-public` scope.
Defaults to false.
imagePath (str):
A fully-qualified path of an image to display for the playlist.
The image must be in JPEG format, and cannot exceed 256KB in Base64 encoded size.
Omit this argument if you do not wish to add a playlist image.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `Playlist` object that contains the playlist details.
"""
apiMethodName:str = 'service_spotify_playlist_create'
apiMethodParms:SIMethodParmListContext = None
result:Playlist = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("userId", userId)
apiMethodParms.AppendKeyValue("name", name)
apiMethodParms.AppendKeyValue("description", description)
apiMethodParms.AppendKeyValue("public", public)
apiMethodParms.AppendKeyValue("collaborative", collaborative)
apiMethodParms.AppendKeyValue("imagePath", imagePath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Create Service", apiMethodParms)
# create Spotify playlist.
_logsi.LogVerbose("Creating Spotify Playlist")
result = self.data.spotifyClient.CreatePlaylist(userId, name, description, public, collaborative, imagePath)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_items_add(
self,
playlistId:str,
uris:str,
position:int=None,
) -> dict:
"""
Add one or more items to a user's playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
uris (str):
A comma-separated list of Spotify URIs to add; can be track or episode URIs.
Example: `spotify:track:4iV5W9uYEdYUVa79Axb7Rh,spotify:episode:512ojhOuo1ktJprKbVcKyQ`.
A maximum of 100 items can be specified in one request.
If nothing is specified, then the track (or episode) uri currently playing is used.
position (int):
The position to insert the items, a zero-based index.
For example, to insert the items in the first position: position=0;
to insert the items in the third position: position=2.
If omitted, the items will be appended to the playlist.
Items are added in the order they are listed in the `uris` argument.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A snapshot ID for the updated playlist.
"""
apiMethodName:str = 'service_spotify_playlist_items_add'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("uris", uris)
apiMethodParms.AppendKeyValue("position", position)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Add Items Service", apiMethodParms)
# validation - if position is -1 then set it to None (treat as append).
if isinstance(position, int) and position == -1:
position = None
# add items to Spotify playlist.
_logsi.LogVerbose("Adding item(s) to Spotify playlist")
result:str = self.data.spotifyClient.AddPlaylistItems(playlistId, uris, position)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_items_clear(
self,
playlistId:str,
) -> dict:
"""
Removes (clears) all items from a user's playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A snapshot ID for the updated playlist.
"""
apiMethodName:str = 'service_spotify_playlist_items_clear'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Clear Items Service", apiMethodParms)
# clear items from Spotify playlist.
_logsi.LogVerbose("Clearing item(s) from Spotify playlist")
result:str = self.data.spotifyClient.ClearPlaylistItems(playlistId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_items_remove(
self,
playlistId:str=None,
uris:str=None,
snapshotId:str=None,
) -> dict:
"""
Remove one or more items from a user's playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
uris (str):
A comma-separated list of Spotify URIs to remove; can be track or episode URIs.
Example: `spotify:track:4iV5W9uYEdYUVa79Axb7Rh,spotify:episode:512ojhOuo1ktJprKbVcKyQ`.
A maximum of 100 items can be specified in one request.
If nothing is specified, then the track (or episode) uri currently playing is used.
snapshotId (str):
The playlist's snapshot ID against which you want to make the changes.
The API will validate that the specified items exist and in the specified positions and
make the changes, even if more recent changes have been made to the playlist.
If null, the current playlist is updated.
Default is null.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A snapshot ID for the updated playlist.
"""
apiMethodName:str = 'service_spotify_playlist_items_remove'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("uris", uris)
apiMethodParms.AppendKeyValue("snapshotId", snapshotId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Remove Items Service", apiMethodParms)
# remove items from Spotify playlist.
_logsi.LogVerbose("Removing item(s) from Spotify playlist")
result:str = self.data.spotifyClient.RemovePlaylistItems(playlistId, uris, snapshotId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_items_reorder(
self,
playlistId:str,
rangeStart:int,
insertBefore:int,
rangeLength:int=1,
snapshotId:str=None,
) -> dict:
"""
Reorder items in a user's playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
rangeStart (int):
The position of the first item to be reordered.
This is a one-offset integer (NOT zero-offset).
insertBefore (int):
The position where the items should be inserted.
To reorder the items to the end of the playlist, simply set `insertBefore`
to the position after the last item.
This is a one-offset integer (NOT zero-offset).
rangeLength (int):
The amount of items to be reordered; defaults to 1 if not set.
The range of items to be reordered begins from the `rangeStart` position, and includes
the `rangeLength` subsequent items.
snapshotId (str):
The playlist's snapshot ID against which you want to make the changes.
If null, the current playlist is updated.
Example: `MTk3LGEzMjUwZGYwODljNmI5ZjAxZTRjZThiOGI4NzZhM2U5M2IxOWUyMDQ`
Default is null.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A snapshot ID for the updated playlist.
"""
apiMethodName:str = 'service_spotify_playlist_items_reorder'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("rangeStart", rangeStart)
apiMethodParms.AppendKeyValue("insertBefore", insertBefore)
apiMethodParms.AppendKeyValue("rangeLength", rangeLength)
apiMethodParms.AppendKeyValue("snapshotId", snapshotId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Reorder Playlist Items Service", apiMethodParms)
# reorder playlist items.
_logsi.LogVerbose("Reorder Spotify Playlist Items")
result = self.data.spotifyClient.ReorderPlaylistItems(playlistId, rangeStart, insertBefore, rangeLength, snapshotId)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_playlist_items_replace(
self,
playlistId:str=None,
uris:str=None,
) -> dict:
"""
Replace one or more items in a user's playlist. Replacing items in a playlist will
overwrite its existing items.
This method can also be used to clear a playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `5AC9ZXA7nJ7oGWO911FuDG`
uris (str):
A comma-separated list of Spotify URIs to replace; can be track or episode URIs.
Example: `spotify:track:4iV5W9uYEdYUVa79Axb7Rh,spotify:episode:26c0zVyOv1lzfYpBXdh1zC`.
A maximum of 100 items can be specified in one request.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A snapshot ID for the updated playlist.
"""
apiMethodName:str = 'service_spotify_playlist_items_replace'
apiMethodParms:SIMethodParmListContext = None
result:dict = {}
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
apiMethodParms.AppendKeyValue("uris", uris)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Replace Playlist Items Service", apiMethodParms)
# replace playlist items.
_logsi.LogVerbose("Replace Spotify Playlist Items")
result = self.data.spotifyClient.ReplacePlaylistItems(playlistId, uris)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_album_favorites(
self,
ids:str=None,
) -> None:
"""
Remove one or more albums from the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the albums.
Maximum: 40 IDs.
Example: `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`
If null, the currently playing track album uri id value is used.
"""
apiMethodName:str = 'service_spotify_remove_album_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove Album Favorites Service", apiMethodParms)
# remove items from Spotify album favorites.
_logsi.LogVerbose("Removing items(s) from Spotify Album Favorites")
self.data.spotifyClient.RemoveAlbumFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_audiobook_favorites(
self,
ids:str=None,
) -> None:
"""
Remove one or more audiobooks from the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the audiobooks.
Maximum: 40 IDs.
Example: `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`
If null, the currently playing audiobook uri id value is used.
"""
apiMethodName:str = 'service_spotify_remove_audiobook_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove Audiobook Favorites Service", apiMethodParms)
# remove items from Spotify audiobook favorites.
_logsi.LogVerbose("Removing items(s) from Spotify Audiobook Favorites")
self.data.spotifyClient.RemoveAudiobookFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_episode_favorites(
self,
ids:str=None,
) -> None:
"""
Remove one or more episodes from the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the episodes.
Maximum: 40 IDs.
Example: `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`
If null, the currently playing episode uri id value is used.
"""
apiMethodName:str = 'service_spotify_remove_episode_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove Episode Favorites Service", apiMethodParms)
# remove items from Spotify episode favorites.
_logsi.LogVerbose("Removing items(s) from Spotify Episode Favorites")
self.data.spotifyClient.RemoveEpisodeFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_show_favorites(
self,
ids:str=None,
) -> None:
"""
Remove one or more shows from the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the shows.
Maximum: 40 IDs.
Example: `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`
If null, the currently playing show uri id value is used.
"""
apiMethodName:str = 'service_spotify_remove_show_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove Show Favorites Service", apiMethodParms)
# remove items from Spotify show favorites.
_logsi.LogVerbose("Removing items(s) from Spotify Show Favorites")
self.data.spotifyClient.RemoveShowFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_track_favorites(
self,
ids:str=None,
) -> None:
"""
Remove one or more tracks from the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the tracks.
Maximum: 40 IDs.
Example: `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`
If null, the currently playing context uri id value is used.
"""
apiMethodName:str = 'service_spotify_remove_track_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove Track Favorites Service", apiMethodParms)
# remove items from Spotify track favorites.
_logsi.LogVerbose("Removing items(s) from Spotify Track Favorites")
self.data.spotifyClient.RemoveTrackFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_remove_user_favorites(
self,
uris:str=None,
) -> None:
"""
Remove one or more items from the current user's library.
Accepts Spotify URIs for tracks, albums, episodes, shows, audiobooks, users, and playlists.
Args:
uris (str):
A comma-separated list of the Spotify URIs for the items.
Maximum: 40 URIs.
Example: `spotify:artist:6APm8EjxOHSYM5B4i3vT3q,spotify:album:6vc9OTcyd3hyzabCmsdnwE,spotify:track:1kWUud3vY5ij5r62zxpTRy`
If null, the currently playing item uri value is used.
"""
apiMethodName:str = 'service_spotify_remove_user_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uris", uris)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Remove User Favorites Service", apiMethodParms)
# remove items from Spotify track favorites.
_logsi.LogVerbose("Removing items(s) from Spotify User Favorites")
self.data.spotifyClient.RemoveUserFavorites(uris)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_album_favorites(
self,
ids:str=None,
) -> None:
"""
Save one or more albums to the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the albums.
Maximum: 40 IDs.
Example: `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`
If null, the currently playing track album uri id value is used.
"""
apiMethodName:str = 'service_spotify_save_album_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save Album Favorites Service", apiMethodParms)
# save items to Spotify album favorites.
_logsi.LogVerbose("Saving items(s) to Spotify Album Favorites")
self.data.spotifyClient.SaveAlbumFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_audiobook_favorites(
self,
ids:str=None,
) -> None:
"""
Save one or more audiobook to the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the audiobooks.
Maximum: 40 IDs.
Example: `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`
If null, the currently playing audiobook uri id value is used.
"""
apiMethodName:str = 'service_spotify_save_audiobook_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save Audiobook Favorites Service", apiMethodParms)
# save items to Spotify audiobook favorites.
_logsi.LogVerbose("Saving items(s) to Spotify Audiobook Favorites")
self.data.spotifyClient.SaveAudiobookFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_episode_favorites(
self,
ids:str=None,
) -> None:
"""
Save one or more episodes to the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the episode.
Maximum: 40 IDs.
Example: `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`
If null, the currently playing episode uri id value is used.
"""
apiMethodName:str = 'service_spotify_save_episode_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save Episode Favorites Service", apiMethodParms)
# save items to Spotify episode favorites.
_logsi.LogVerbose("Saving items(s) to Spotify Episode Favorites")
self.data.spotifyClient.SaveEpisodeFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_show_favorites(
self,
ids:str=None,
) -> None:
"""
Save one or more show to the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the shows.
Maximum: 40 IDs.
Example: `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`
If null, the currently playing show uri id value is used.
"""
apiMethodName:str = 'service_spotify_save_show_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save Show Favorites Service", apiMethodParms)
# save items to Spotify show favorites.
_logsi.LogVerbose("Saving items(s) to Spotify Show Favorites")
self.data.spotifyClient.SaveShowFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_track_favorites(
self,
ids:str=None,
) -> None:
"""
Save one or more tracks to the current user's 'Your Library'.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the tracks.
Maximum: 40 IDs.
Example: `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`
If null, the currently playing context uri id value is used.
"""
apiMethodName:str = 'service_spotify_save_track_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save Track Favorites Service", apiMethodParms)
# save items to Spotify track favorites.
_logsi.LogVerbose("Saving items(s) to Spotify Track Favorites")
self.data.spotifyClient.SaveTrackFavorites(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_save_user_favorites(
self,
uris:str=None,
) -> None:
"""
Add one or more items to the current user's library.
Accepts Spotify URIs for tracks, albums, episodes, shows, audiobooks, users, and playlists.
Args:
uris (str):
A comma-separated list of the Spotify URIs for the items.
Maximum: 40 URIs.
Example: `spotify:artist:6APm8EjxOHSYM5B4i3vT3q,spotify:album:6vc9OTcyd3hyzabCmsdnwE,spotify:track:1kWUud3vY5ij5r62zxpTRy`
If null, the currently playing item uri value is used.
"""
apiMethodName:str = 'service_spotify_save_user_favorites'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("uris", uris)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Save User Favorites Service", apiMethodParms)
# save items to Spotify user favorites.
_logsi.LogVerbose("Saving items(s) to Spotify User Favorites")
self.data.spotifyClient.SaveUserFavorites(uris)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_all(
self,
criteria:str,
criteriaType:str=None,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about albums, artists, playlists, tracks, shows, episodes
or audiobooks that match a keyword string. Audiobooks are only available within the US, UK,
Canada, Ireland, New Zealand and Australia markets.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
criteriaType (str):
A comma-separated list of item types to search across.
Search results include hits from all the specified item types.
For example: "album,track" returns both albums and tracks matching criteria argument.
Allowed values: "album", "artist", "playlist", "track", "show", "episode", "audiobook".
Default: "track"
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request, per criteria type.
Paging is automatically used to retrieve all available items up to the
maximum number specified per type.
Default: 20
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `SearchResponse` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_all'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("criteriaType", criteriaType)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search All Service", apiMethodParms)
# get Spotify catalog information about objects that match a keyword string.
_logsi.LogVerbose("Searching Spotify for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.Search(criteria, criteriaType, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_albums(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about albums that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `AlbumPageSimplified` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_albums'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Albums Service", apiMethodParms)
# get Spotify catalog information about Albums that match a keyword string.
_logsi.LogVerbose("Searching Spotify Albums for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchAlbums(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Albums.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_artists(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about artists that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ArtistPage` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_artists'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Artists Service", apiMethodParms)
# get Spotify catalog information about Artists that match a keyword string.
_logsi.LogVerbose("Searching Spotify Artists for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchArtists(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Artists.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_audiobooks(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about audiobooks that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `AudiobookPageSimplified` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_audiobooks'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Audiobooks Service", apiMethodParms)
# get Spotify catalog information about Audiobooks that match a keyword string.
_logsi.LogVerbose("Searching Spotify Audiobooks for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchAudiobooks(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Audiobooks.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_episodes(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about episodes that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `EpisodePageSimplified` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_episodes'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Episodes Service", apiMethodParms)
# get Spotify catalog information about Episodes that match a keyword string.
_logsi.LogVerbose("Searching Spotify Episodes for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchEpisodes(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Episodes.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_playlists(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about playlists that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `PlaylistPageSimplified` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_playlists'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Playlists Service", apiMethodParms)
# get Spotify catalog information about Playlists that match a keyword string.
_logsi.LogVerbose("Searching Spotify Playlists for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchPlaylists(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Playlists.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_shows(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about shows (aka. podcasts) that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An `ShowPageSimplified` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_shows'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Shows Service", apiMethodParms)
# get Spotify catalog information about Episodes that match a keyword string.
_logsi.LogVerbose("Searching Spotify Shows for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchShows(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Shows.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_search_tracks(
self,
criteria:str,
limit:int=20,
offset:int=0,
market:str=None,
includeExternal:str=None,
limitTotal:int=None,
) -> dict:
"""
Get Spotify catalog information about tracks that match a keyword string.
Args:
criteria (str):
Your search query.
You can narrow down your search using field filters.
The available filters are album, artist, track, year, upc, tag:hipster, tag:new,
isrc, and genre. Each field filter only applies to certain result types.
The artist and year filters can be used while searching albums, artists and tracks.
You can filter on a single year or a range (e.g. 1955-1960).
The album filter can be used while searching albums and tracks.
The genre filter can be used while searching artists and tracks.
The isrc and track filters can be used while searching tracks.
The upc, tag:new and tag:hipster filters can only be used while searching albums.
The tag:new filter will return albums released in the past two weeks and tag:hipster
can be used to return only albums with the lowest 10% popularity.
limit (int):
The maximum number of items to return in a page of items.
Default: 20, Range: 1 to 50.
offset (int):
The page index offset of the first item to return.
Use with limit to get the next set of items.
Default: 0 (the first item). Range: 0 to 1000.
market (str):
An ISO 3166-1 alpha-2 country code. If a country code is specified, only content that
is available in that market will be returned. If a valid user access token is specified
in the request header, the country associated with the user account will take priority over
this parameter.
Note: If neither market or user country are provided, the content is considered unavailable for the client.
Users can view the country that is associated with their account in the account settings.
Example: `ES`
includeExternal (str):
If "audio" is specified it signals that the client can play externally hosted audio content, and
marks the content as playable in the response. By default externally hosted audio content is marked
as unplayable in the response.
Allowed values: "audio"
limitTotal (int):
The maximum number of items to return for the request.
If specified, this argument overrides the limit and offset argument values
and paging is automatically used to retrieve all available items up to the
maximum number specified.
Default: None (disabled)
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `TrackPage` object of matching results.
"""
apiMethodName:str = 'service_spotify_search_tracks'
apiMethodParms:SIMethodParmListContext = None
result:SearchResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("criteria", criteria)
apiMethodParms.AppendKeyValue("limit", limit)
apiMethodParms.AppendKeyValue("offset", offset)
apiMethodParms.AppendKeyValue("market", market)
apiMethodParms.AppendKeyValue("includeExternal", includeExternal)
apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Search Tracks Service", apiMethodParms)
# get Spotify catalog information about Playlists that match a keyword string.
_logsi.LogVerbose("Searching Spotify Tracks for criteria")
searchResponse:SearchResponse = self.data.spotifyClient.SearchTracks(criteria, limit, offset, market, includeExternal, limitTotal)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": searchResponse.Tracks.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_trigger_scan_interval(
self,
) -> None:
"""
Triggers a scan interval sequence, which will update HA State values
from content currently being played on the user's Spotify account.
"""
apiMethodName:str = 'service_spotify_trigger_scan_interval'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
_logsi.LogMethodParmList(SILevel.Verbose, "Trigger Scan Interval Sequence", apiMethodParms)
# force a scan window at the next interval.
_logsi.LogVerbose("'%s': Forcing a playerState scan window for the next %d updates" % (self.name, SPOTIFY_SCAN_INTERVAL_COMMAND))
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_COMMAND
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_unfollow_artists(
self,
ids:str=None,
) -> None:
"""
Remove the current user as a follower of one or more artists.
Args:
ids (str):
A comma-separated list of the Spotify IDs for the artists.
Maximum: 40 IDs.
Example: `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`
If null, the currently playing track artist uri id value is used.
"""
apiMethodName:str = 'service_spotify_unfollow_artists'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Unfollow Artists Service", apiMethodParms)
# unfollow artist(s).
_logsi.LogVerbose("Removing items(s) from Spotify Artist Favorites")
self.data.spotifyClient.UnfollowArtists(ids)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_unfollow_playlist(
self,
playlistId:str=None,
) -> None:
"""
Remove the current user as a follower of a playlist.
Args:
playlistId (str):
The Spotify ID of the playlist.
Example: `3cEYpjA9oz9GiPac4AsH4n`
If null, the currently playing playlist uri id value is used.
"""
apiMethodName:str = 'service_spotify_unfollow_playlist'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("playlistId", playlistId)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Unfollow Playlist Service", apiMethodParms)
# unfollow playlist.
_logsi.LogVerbose("Removing items from Spotify Playlist Favorites")
self.data.spotifyClient.UnfollowPlaylist(playlistId)
# update ha state.
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_unfollow_users(
self,
ids:str=None,
) -> None:
"""
Remove the current user as a follower of one or more users.
Args:
ids (str):
A comma-separated list of Spotify user IDs.
A maximum of 50 IDs can be sent in one request.
Example: `smedjan`
"""
apiMethodName:str = 'service_spotify_unfollow_users'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("ids", ids)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Unfollow Users Service", apiMethodParms)
# unfollow user(s).
_logsi.LogVerbose("Removing items(s) from Spotify Users Favorites")
self.data.spotifyClient.UnfollowUsers(ids)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_zeroconf_device_connect(
self,
username:str,
password:str,
loginid:str,
hostIpv4Address:str,
hostIpPort:str,
cpath:str,
version:str='1.0',
useSSL:bool=False,
preDisconnect:bool=False,
verifyDeviceListEntry:bool=False,
delay:float=0.50,
) -> dict:
"""
Calls the `addUser` Spotify Zeroconf API endpoint to issue a call to SpConnectionLoginBlob. If successful,
the associated device id is added to the Spotify Connect active device list for the specified user account.
Args:
username (str):
Spotify Connect user name to login with (e.g. "yourspotifyusername").
This MUST match the account name (or one of them) that was used to configure Spotify Connect
on the manufacturer device.
password (str):
Spotify Connect user password to login with.
loginId (str):
Spotify Connect login id to login with (e.g. "31l77fd87g8h9j00k89f07jf87ge").
This is also known as the canonical user id value.
This MUST be the value that relates to the `username` argument.
hostIpv4Address (str):
IPV4 address (as a string) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "192.168.1.81").
hostIpPort (int):
Port number (as an integer) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "8200").
cpath (str):
Spotify Connect Zeroconf API CPath property value (e.g. "/zc").
version (str):
Spotify Connect Zeroconf API version number that the device supports (e.g. "2.10.0").
Default is '1.0'.
useSSL (bool):
True if the host device utilizes HTTPS Secure Sockets Layer (SSL) support;
otherwise, False to utilize HTTP.
Default is False (HTTP).
preDisconnect (bool):
True if a Disconnect should be made prior to the Connect call. This will ensure that the
active user is logged out, which must be done if switching user accounts;
otherwise, False to not issue a Disconnect call.
Default is False.
verifyDeviceListEntry (bool):
True to ensure that the device id is present in the Spotify Connect device list before
issuing a call to Connect; otherwise, False to always call Connect to add the device.
Default is False.
delay (float):
Time delay (in seconds) to wait AFTER issuing a command to the device.
This delay will give the spotify zeroconf api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
The login (on the device) is performed asynchronously, so the return result only indicates whether the library
is able to perform the login attempt. You should issue a call to the Spotify Web API `Get Available Devices`
endpoint to check the current device list to ensure that the device id was successfully added or not.
Use the `verifyDeviceListEntry` argument to check if the device is currently listed in the Spotify Connect
device list. If True, a `GetInformation` call is issued to get the device id, and a `GetPlayerDevice` call
is made to check if the device id is in the Spotify Connect device list. If the device id is found in the
Spotify Connect device list then the Connect command is not issued; if the device id is NOT found in the Spotify
Connect device list then the Connect command is issued
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `ZeroconfResponse` object that contains the response.
"""
apiMethodName:str = 'service_spotify_zeroconf_device_connect'
apiMethodParms:SIMethodParmListContext = None
result:ZeroconfResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("hostIpv4Address", hostIpv4Address)
apiMethodParms.AppendKeyValue("hostIpPort", hostIpPort)
apiMethodParms.AppendKeyValue("cpath", cpath)
apiMethodParms.AppendKeyValue("version", version)
apiMethodParms.AppendKeyValue("useSSL", useSSL)
apiMethodParms.AppendKeyValue("username", username)
apiMethodParms.AppendKeyValue("password (with mask)", passwordMaskString(password))
apiMethodParms.AppendKeyValue("loginid", loginid)
apiMethodParms.AppendKeyValue("preDisconnect", preDisconnect)
apiMethodParms.AppendKeyValue("verifyDeviceListEntry", verifyDeviceListEntry)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect ZeroConf Device Connect Service", apiMethodParms)
# validations.
if (preDisconnect is None):
preDisconnect = False
if (verifyDeviceListEntry is None):
verifyDeviceListEntry = False
# create Spotify Zeroconf API connection object for the device.
zconn:ZeroconfConnect = ZeroconfConnect(
hostIpAddress=hostIpv4Address,
hostIpPort=hostIpPort,
cpath=cpath,
version=version,
useSSL=useSSL,
tokenStorageDir=self.data.spotifyClient.TokenStorageDir,
tokenStorageFile=self.data.spotifyClient.TokenStorageFile,
tokenAuthInBrowser=False)
# are we verifying if the device id is already in the Spotify Connect device list?
if (verifyDeviceListEntry):
# trace.
_logsi.LogVerbose("'%s': Checking current Spotify Connect device list for Device ID (%s) ..." % (self.name, zconn.Uri))
# get the device id of the device.
info:ZeroconfGetInfo = zconn.GetInformation()
# is the device in the current Spotify Connect device list? if so, then we are done.
playerDevice:PlayerDevice = self.data.spotifyClient.GetPlayerDevice(info.DeviceId, True)
if playerDevice is not None:
_logsi.LogVerbose("'%s': Device ID '%s' (%s) is already in the Spotify Connect device list; nothing to do" % (self.name, playerDevice.Id, playerDevice.Name))
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": info.ToDictionary()
}
# trace.
_logsi.LogVerbose("'%s': Device ID '%s' (%s) was not found in the Spotify Connect device list; device will be activated" % (self.name, info.DeviceId, info.RemoteName))
# disconnect the device from Spotify Connect.
if (preDisconnect == True):
result = zconn.Disconnect(delay)
# default user credentials if not specified. these are the same credentials that
# are stored in the configuration options settings, which were loaded when the
# spotifyClient instance was created.
if (username is None):
username = self.data.spotifyClient._SpotifyConnectUsername
if (password is None):
password = self.data.spotifyClient._SpotifyConnectPassword
if (loginid is None):
loginid = self.data.spotifyClient._SpotifyConnectLoginId
# connect the device to Spotify Connect, which should make it known to any available
# Spotify Connect player clients.
result = zconn.Connect(username, password, loginid, delay)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyZeroconfApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException("'%s': MediaPlayer %s exception: %s" % (self.name, apiMethodName, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_zeroconf_device_disconnect(
self,
hostIpv4Address:str,
hostIpPort:str,
cpath:str,
version:str='1.0',
useSSL:bool=False,
delay:float=0.50,
) -> dict:
"""
Calls the `resetUsers` Spotify Zeroconf API endpoint to issue a call to SpConnectionLogout.
The currently logged in user (if any) will be logged out of Spotify Connect, and the
device id removed from the active Spotify Connect device list.
Args:
hostIpv4Address (str):
IPV4 address (as a string) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "192.168.1.81").
hostIpPort (int):
Port number (as an integer) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "8200").
cpath (str):
Spotify Connect Zeroconf API CPath property value (e.g. "/zc").
version (str):
Spotify Connect Zeroconf API version number that the device supports (e.g. "2.10.0").
Default is '1.0'.
useSSL (bool):
True if the host device utilizes HTTPS Secure Sockets Layer (SSL) support;
otherwise, False to utilize HTTP.
Default is False (HTTP).
delay (float):
Time delay (in seconds) to wait AFTER issuing a command to the device.
This delay will give the spotify zeroconf api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
The URI value consists of an IP Address, port, CPath, and version value that are used to send
requests to / receive responses from a headless Spotify Connect device. These values can be
obtained from a Zeroconf discovery process.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `ZeroconfResponse` object that contains the response.
"""
apiMethodName:str = 'service_spotify_zeroconf_device_disconnect'
apiMethodParms:SIMethodParmListContext = None
result:ZeroconfResponse = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("hostIpv4Address", hostIpv4Address)
apiMethodParms.AppendKeyValue("hostIpPort", hostIpPort)
apiMethodParms.AppendKeyValue("cpath", cpath)
apiMethodParms.AppendKeyValue("version", version)
apiMethodParms.AppendKeyValue("useSSL", useSSL)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect ZeroConf Device Disconnect Service", apiMethodParms)
# create Spotify Zeroconf API connection object for the device.
zconn:ZeroconfConnect = ZeroconfConnect(
hostIpAddress=hostIpv4Address,
hostIpPort=hostIpPort,
cpath=cpath,
version=version,
useSSL=useSSL,
tokenStorageDir=self.data.spotifyClient.TokenStorageDir,
tokenStorageFile=self.data.spotifyClient.TokenStorageFile,
tokenAuthInBrowser=False)
# disconnect the device from Spotify Connect.
result = zconn.Disconnect(delay)
# update ha state.
self._attr_state = MediaPlayerState.IDLE
self.schedule_update_ha_state(force_refresh=False)
# device was disconnected, so force a scan window at the next interval.
self._commandScanInterval = SPOTIFY_SCAN_INTERVAL_TRACK_ENDSTART
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyZeroconfApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException("'%s': MediaPlayer %s exception: %s" % (self.name, apiMethodName, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_zeroconf_device_getinfo(
self,
hostIpv4Address:str,
hostIpPort:str,
cpath:str,
version:str='1.0',
useSSL:bool=False
) -> dict:
"""
Calls the `getInfo` Spotify Zeroconf API endpoint to return information about the device.
Args:
hostIpv4Address (str):
IPV4 address (as a string) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "192.168.1.81").
hostIpPort (int):
Port number (as an integer) at which the Spotify Connect Zeroconf API can be reached
on the Spotify Connect device (e.g. "8200").
cpath (str):
Spotify Connect Zeroconf API CPath property value (e.g. "/zc").
version (str):
Spotify Connect Zeroconf API version number that the device supports (e.g. "2.10.0").
Default is '1.0'.
useSSL (bool):
True if the host device utilizes HTTPS Secure Sockets Layer (SSL) support;
otherwise, False to utilize HTTP.
Default is False (HTTP).
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: A `ZeroconfGetInfo` object that contains the response.
"""
apiMethodName:str = 'service_spotify_zeroconf_getinfo'
apiMethodParms:SIMethodParmListContext = None
result:ZeroconfGetInfo = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("hostIpv4Address", hostIpv4Address)
apiMethodParms.AppendKeyValue("hostIpPort", hostIpPort)
apiMethodParms.AppendKeyValue("cpath", cpath)
apiMethodParms.AppendKeyValue("version", version)
apiMethodParms.AppendKeyValue("useSSL", useSSL)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect ZeroConf Device Get Information Service", apiMethodParms)
# create Spotify Zeroconf API connection object for the device.
zconn:ZeroconfConnect = ZeroconfConnect(
hostIpAddress=hostIpv4Address,
hostIpPort=hostIpPort,
cpath=cpath,
version=version,
useSSL=useSSL,
tokenStorageDir=self.data.spotifyClient.TokenStorageDir,
tokenStorageFile=self.data.spotifyClient.TokenStorageFile,
tokenAuthInBrowser=False)
# get Spotify Connect device information.
result = zconn.GetInformation()
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": result.ToDictionary()
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyZeroconfApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException("'%s': MediaPlayer %s exception: %s" % (self.name, apiMethodName, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_spotify_zeroconf_discover_devices(
self,
timeout:int=5,
) -> dict:
"""
Discover Spotify Connect devices on the local network via the
ZeroConf (aka MDNS) service, and return details about each device.
Args:
timeout (int):
Maximum amount of time to wait (in seconds) for the
discovery to complete.
Default is 5 seconds.
Returns:
A dictionary that contains the following keys:
- user_profile: A (partial) user profile that retrieved the result.
- result: An array of `ZeroconfDiscoveryResult` objects of matching results.
"""
apiMethodName:str = 'service_spotify_zeroconf_discover_devices'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("timeout", timeout)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify ZeroConf Discover Devices Service", apiMethodParms)
# create a new instance of the discovery class.
# do not verify device connections;
# do not print device details to the console as they are discovered.
discovery:SpotifyDiscovery = SpotifyDiscovery(self.data.spotifyClient.ZeroconfClient, printToConsole=False)
# discover Spotify Connect devices on the network, waiting up to the specified
# time in seconds for all devices to be discovered.
discovery.DiscoverDevices(timeout)
# return the (partial) user profile that retrieved the result, as well as the result itself.
return {
"user_profile": self._GetUserProfilePartialDictionary(self.data.spotifyClient.UserProfile),
"result": [ item.ToDictionary() for item in discovery.DiscoveryResults ]
}
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException("'%s': MediaPlayer %s exception: %s" % (self.name, apiMethodName, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_test_token_expire(
self,
) -> None:
"""
Forces Spotify authorization token to expire.
Note that this will only expire the `SpotifyClient.AuthToken` token;
It will NOT expire the `session.token` token!
"""
apiMethodName:str = 'service_test_token_expire'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
_logsi.LogMethodParmList(SILevel.Verbose, "TEST-SERVICE - Token Expire Service", apiMethodParms)
# force Spotify authentication token expiration.
_logsi.LogWarning("'%s': Forcing token expiration for the next Spotify Web API call for testing purposes" % self.name, colorValue=SIColors.Red)
unix_epoch = datetime(1970, 1, 1)
dtUtcNow:datetime = datetime.utcnow()
self.data.spotifyClient.AuthToken._ExpireDateTimeUtc = dtUtcNow + timedelta(seconds=-10)
self.data.spotifyClient.AuthToken._ExpiresAt = int((dtUtcNow - unix_epoch).total_seconds() - 10) # -10 seconds from epoch, current date
_logsi.LogObject(SILevel.Verbose, "'%s': SpotifyAuthToken object (post-update)" % self.name, self.data.spotifyClient.AuthToken, excludeNonPublic=True, colorValue=SIColors.Red)
# force OAuth2Session token expiration.
self.data.spotifyClient._AuthClient.Session.token['expires_at'] = int((dtUtcNow - unix_epoch).total_seconds() - 10) # -10 seconds from epoch, current date
_logsi.LogDictionary(SILevel.Verbose, "'%s': OAuth2Session token dictionary (post-update)" % self.name, self.data.spotifyClient._AuthClient.Session.token, prettyPrint=True, colorValue=SIColors.Red)
_logsi.LogWarning("'%s': Token Expire Service complete; token should be refreshed on the next Spotify Web API call" % self.name, colorValue=SIColors.Red)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
def service_volume_set_step(
self,
level:float=None,
level_percent:int=None,
) -> None:
"""
Set level used for volume step services.
Args:
level (float):
Level percentage to adjust the volume by, expressed as a float value.
Range is 0.01 to 1.0; Default is None.
level_percent (int):
Level percentage to adjust the volume by, expressed as an integer value.
`level` argument is ignored if this argument is specified.
Range is 1 to 100; Default is None.
Default is 0.10 if neither argument is specified.
A system log warning is issued if level is less than 0.01 or greater than 1.0,
and the level is defaulted to 0.10.
"""
apiMethodName:str = 'service_volume_set_step'
apiMethodParms:SIMethodParmListContext = None
try:
# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("level", level)
apiMethodParms.AppendKeyValue("level_percent", level_percent)
_logsi.LogMethodParmList(SILevel.Verbose, "Volume Set Step Service", apiMethodParms)
# default value if no argument was specified.
stepValue:float = 0.10
# validations.
if (level is not None):
if (isinstance(level, int)):
level = float(level)
if (not isinstance(level, float)):
level = 0.10
if (isinstance(level, float)):
stepValue = level
# if level_percent specified, then use it instead and convert to float value.
if (level_percent is not None):
if (not isinstance(level_percent, int)):
level_percent = 10
if level_percent < 1:
level_percent = 10
if level_percent > 100:
level_percent = 10
stepValue = float(level_percent / 100)
_logsi.LogVerbose("'%s': level_percent value specified; converted percent value %s to float value %s" % (self.name, level_percent, stepValue))
# ensure range is between 0.01 and 1.0.
if (stepValue < 0.01) or (stepValue > 1.0):
_logsi.LogWarning(f'Volume Step level \"{stepValue:.3f}\" was not in the range of 0.01 to 1.0; defaulting to 0.10')
stepValue = 0.10
# trace.
_logsi.LogVerbose("'%s': Volume step level: %s" % (self.name, stepValue))
# update ha state.
self._attr_volume_step = stepValue
self.schedule_update_ha_state(force_refresh=False)
# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise ServiceValidationError(ex.Message)
except SpotifyWebApiError as ex:
raise ServiceValidationError(ex.Message)
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
async def async_added_to_hass(self) -> None:
"""
Run when this Entity has been added to HA.
Importantly for a push integration, the module that will be getting updates
needs to notify HA of changes. If a DataUpdateCoordinator instance was created,
then we will register a callback method here so that we can forward the change
notifications on to Home Assistant (e.g. a call to `self.async_write_ha_state`).
The call back registration is done here once this entity is registered with Home
Assistant (rather than in the `__init__` method).
"""
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
# call base class method.
await super().async_added_to_hass()
# nothing to do here.
# left this here in case we add a DataUpdateCoordinator later.
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
async def async_will_remove_from_hass(self) -> None:
"""
Entity being removed from hass (the opposite of async_added_to_hass).
Remove any registered call backs here.
"""
try:
# trace.
_logsi.EnterMethod(SILevel.Debug)
_logsi.LogVerbose("'%s': removing instance from hass" % self.name)
# nothing to do here.
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
async def async_browse_media(
self,
media_content_type: MediaType | str | None = None,
media_content_id: str | None = None,
) -> BrowseMedia:
"""
Implement the websocket media browsing helper.
"""
methodParms:SIMethodParmListContext = None
try:
# trace.
methodParms = _logsi.EnterMethodParmList(SILevel.Debug)
methodParms.AppendKeyValue("media_content_type", media_content_type)
methodParms.AppendKeyValue("media_content_id", media_content_id)
_logsi.LogMethodParmList(SILevel.Verbose, "'%s': MediaPlayer is browsing for media content type '%s'" % (self.name, media_content_type), methodParms)
# browse spotifysplus device media.
if media_content_type is None and media_content_id is None:
# handle initial media browser selection (e.g. show the starting index).
_logsi.LogVerbose("'%s': MediaPlayer is browsing main media library index content id '%s'" % (self.name, media_content_id))
return await async_browse_media_library_index(
self.hass,
self.data.spotifyClient,
self.name,
self.source,
SPOTIFY_LIBRARY_MAP,
BrowsableMedia.SPOTIFY_LIBRARY_INDEX,
media_content_type,
media_content_id,
)
elif media_content_type == 'favorites':
# ignore Sonos-Card "favorites" node queries.
_logsi.LogVerbose("'%s': ignoring Sonos-Card favorites query (no SoundTouch equivalent)" % self.name)
# Sonos-Card requires a valid BrowseMedia object, so return an empty one.
browseMedia:BrowseMedia = BrowseMedia(
can_expand=False,
can_play=False,
children=[],
children_media_class=None,
media_class=None,
media_content_id=media_content_id,
media_content_type=media_content_type,
title="Favorites not supported",
)
return browseMedia
else:
# handle spotifysplus media library selection.
# note that this is NOT async, as SpotifyClient is not async!
_logsi.LogVerbose("'%s': MediaPlayer is browsing media node content id '%s'" % (self.name, media_content_id))
return await self.hass.async_add_executor_job(
browse_media_node,
self.hass,
self.data.spotifyClient,
self.name,
self.source,
SPOTIFY_LIBRARY_MAP,
media_content_type,
media_content_id,
)
except Exception as ex:
# trace.
_logsi.LogException("'%s': MediaPlayer async_browse_media exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)
async def async_search_media(self, query: SearchMediaQuery) -> SearchMedia:
"""
Implement the media search helper.
Args:
query (SearchMediaQuery):
A `SearchMediaQuery` object that contains search parameters.
Some parameters may have null values, depending on what the user is asking for.
Returns:
A list of `BrowseMedia` objects that contain the search results.
Note that this differs from the method signature, which indicates a `SearchMedia`
object is returned (it is not)!
"""
methodParms:SIMethodParmListContext = None
try:
# trace.
methodParms = _logsi.EnterMethodParmList(SILevel.Debug)
methodParms.AppendKeyValue("query.media_content_id", query.media_content_id)
methodParms.AppendKeyValue("query.media_content_type", query.media_content_type)
methodParms.AppendKeyValue("query.media_filter_classes", query.media_filter_classes)
methodParms.AppendKeyValue("query.search_query", query.search_query)
_logsi.LogMethodParmList(SILevel.Verbose, "'%s': MediaPlayer is searching for media content types '%s'" % (self.name, query.media_content_type), methodParms)
# handle spotifysplus media library selection.
# note that this is NOT async, as SpotifyClient is not async!
_logsi.LogVerbose("'%s': MediaPlayer is searching media node content id '%s'" % (self.name, query.media_content_id))
return await self.hass.async_add_executor_job(
search_media_node,
self.hass,
self.data.spotifyClient,
self.name,
query,
)
except Exception as ex:
# trace.
_logsi.LogException("'%s': MediaPlayer async_search_media exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
raise IntegrationError(str(ex)) from ex
finally:
# trace.
_logsi.LeaveMethod(SILevel.Debug)