From a51c5ed7f2a47ff9e54388dae4ccc2fbb4388693 Mon Sep 17 00:00:00 2001 From: Todd Lucas Date: Mon, 16 Sep 2024 13:57:31 -0500 Subject: [PATCH] [ 1.0.55 ] * Added service `check_album_favorites` to check if one or more albums (or the currently playing album) exists in the current user's 'Your Library' favorites. * Added service `check_artists_following` to check if one or more artists (or the currently playing artist) exists in the current user's 'Your Library' favorites. * Added service `check_audiobook_favorites` to check if one or more audiobooks (or the currently playing audiobook) exists in the current user's 'Your Library' favorites. * Added service `check_episode_favorites` to check if one or more episodes (or the currently playing episode) exists in the current user's 'Your Library' favorites. * Added service `check_show_favorites` to check if one or more shows (or the currently playing show) exists in the current user's 'Your Library' favorites. * Added service `check_track_favorites` to check if one or more tracks (or the currently playing track) exists in the current user's 'Your Library' favorites. * Added service `get_tracks_audio_features` to get audio features for multiple tracks based on their Spotify IDs. * Updated service `follow_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. * Updated service `get_album` to make the `albumId` argument optional; if not supplied, the currently playing album id value is used instead. * Updated service `get_artist` to make the `artistId` argument optional; if not supplied, the currently playing artist id value is used instead. * Updated service `get_artist_albums` to make the `artistId` argument optional; if not supplied, the currently playing artist id value is used instead. * Updated service `get_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. * Updated service `get_show` to make the `showId` argument optional; if not supplied, the currently playing show id value is used instead. * Updated service `get_show_episodes` to make the `showId` argument optional; if not supplied, the currently playing show id value is used instead. * Updated service `unfollow_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. * Added the following extra state attributes: `sp_playlist_name`, `sp_playlist_uri`, `sp_user_country`, `sp_user_display-name`, `sp_user_email`, `sp_user_id`, `sp_user_product`, `sp_user_uri`. * Updated underlying `spotifywebapiPython` package requirement to version 1.0.95. --- CHANGELOG.md | 20 + README.md | 71 +-- custom_components/spotifyplus/__init__.py | 226 +++++++- custom_components/spotifyplus/manifest.json | 4 +- custom_components/spotifyplus/media_player.py | 496 ++++++++++++++++-- custom_components/spotifyplus/services.yaml | 183 ++++++- custom_components/spotifyplus/strings.json | 116 +++- .../spotifyplus/translations/en.json | 116 +++- hacs.json | 13 +- requirements.txt | 2 +- 10 files changed, 1064 insertions(+), 183 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb4254..8029ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,26 @@ Change are listed in reverse chronological order (newest to oldest). +###### [ 1.0.55 ] - 2024/09/16 + + * Added service `check_album_favorites` to check if one or more albums (or the currently playing album) exists in the current user's 'Your Library' favorites. + * Added service `check_artists_following` to check if one or more artists (or the currently playing artist) exists in the current user's 'Your Library' favorites. + * Added service `check_audiobook_favorites` to check if one or more audiobooks (or the currently playing audiobook) exists in the current user's 'Your Library' favorites. + * Added service `check_episode_favorites` to check if one or more episodes (or the currently playing episode) exists in the current user's 'Your Library' favorites. + * Added service `check_show_favorites` to check if one or more shows (or the currently playing show) exists in the current user's 'Your Library' favorites. + * Added service `check_track_favorites` to check if one or more tracks (or the currently playing track) exists in the current user's 'Your Library' favorites. + * Added service `get_tracks_audio_features` to get audio features for multiple tracks based on their Spotify IDs. + * Updated service `follow_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. + * Updated service `get_album` to make the `albumId` argument optional; if not supplied, the currently playing album id value is used instead. + * Updated service `get_artist` to make the `artistId` argument optional; if not supplied, the currently playing artist id value is used instead. + * Updated service `get_artist_albums` to make the `artistId` argument optional; if not supplied, the currently playing artist id value is used instead. + * Updated service `get_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. + * Updated service `get_show` to make the `showId` argument optional; if not supplied, the currently playing show id value is used instead. + * Updated service `get_show_episodes` to make the `showId` argument optional; if not supplied, the currently playing show id value is used instead. + * Updated service `unfollow_playlist` to make the `playlistId` argument optional; if not supplied, the currently playing playlist id value is used instead. + * Added the following extra state attributes: `sp_playlist_name`, `sp_playlist_uri`, `sp_user_country`, `sp_user_display-name`, `sp_user_email`, `sp_user_id`, `sp_user_product`, `sp_user_uri`. + * Updated underlying `spotifywebapiPython` package requirement to version 1.0.95. + ###### [ 1.0.54 ] - 2024/08/22 * Updated `config_flow` to correct a bug in the token authorization process that was introduced in v1.0.52 with the Token Cache file location change. This was preventing new instances of the integration to be setup due to `connection_error` exceptions in the Spotify OAuth2 authorization method. diff --git a/README.md b/README.md index 9337b5b..09dbbb3 100644 --- a/README.md +++ b/README.md @@ -33,74 +33,13 @@ The following Home Assistant media_player Platform services are supplied by this - VOLUME_SET - VOLUME_STEP -The following custom services are also supplied by this integration. -- Follow Artists -- Follow Playlist -- Follow Users -- Get Album -- Get Album Favorites -- Get Album New Releases -- Get Artist -- Get Artists Albums -- Get Artists Followed -- Get Browse Categories -- Get Category Playlists -- Get Featured Playlists -- Get Player Devices -- Get Player Now Playing -- Get Player Playback State -- Get Player Queue Info -- Get Player Recent Tracks -- Get Playlist -- Get Playlist Favorites -- Get Show -- Get Show Episodes -- Get Show Favorites -- Get Spotify Connect Devices -- Get Track Favorites -- Get Users Top Artists -- Get Users Top Tracks -- Player Activate Devices -- Player Media Play Context -- Player Media Play Track Favorites -- Player Media Play Tracks -- Player Resolve Device Id -- Player Transfer Playback -- Playlist Change -- Playlist Cover Image Add -- Playlist Create -- Playlist Items Add -- Playlist Items Clear -- Playlist Items Remove -- Remove Album Favorites -- Remove Audiobook Favorites -- Remove Episode Favorites -- Remove Show Favorites -- Remove Track Favorites -- Save Album Favorites -- Save Audiobook Favorites -- Save Episode Favorites -- Save Show Favorites -- Save Track Favorites -- Search Albums -- Search Artists -- Search Audiobooks -- Search Episodes -- Search Playlists -- Search Shows -- Search Tracks -- Unfollow Artists -- Unfollow Playlist -- Unfollow Users -- ZeroConf Device Connect -- ZeroConf Device Disconnect -- ZeroConf Device GetInformation -- ZeroConf Discover Devices - -Check out the [Services Provided wiki](https://github.com/thlucas1/homeassistantcomponent_spotifyplus/wiki/Services-Provided) page for detailed explanations and YAML examples of the custom services provided by this integration. - Check out the [Media Player Service Enhancements wiki](https://github.com/thlucas1/homeassistantcomponent_spotifyplus/wiki/Media-Player-Service-Enhancements) page for detailed explanations and YAML examples of the media player service enhancements provided by this integration. +## Custom Services + +This integration provides 99% of the services offered by the Spotify Web API. +Check out the [Services Provided wiki](https://github.com/thlucas1/homeassistantcomponent_spotifyplus/wiki/Services-Provided) page for detailed explanations and YAML examples. + ## HACS Installation Instructions - go to HACS main menu. diff --git a/custom_components/spotifyplus/__init__.py b/custom_components/spotifyplus/__init__.py index 171db3b..d4484a3 100644 --- a/custom_components/spotifyplus/__init__.py +++ b/custom_components/spotifyplus/__init__.py @@ -86,6 +86,12 @@ # ----------------------------------------------------------------------------------- # Custom Service Schemas. # ----------------------------------------------------------------------------------- +SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES:str = 'check_album_favorites' +SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING:str = 'check_artists_following' +SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES:str = 'check_audiobook_favorites' +SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES:str = 'check_episode_favorites' +SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES:str = 'check_show_favorites' +SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES:str = 'check_track_favorites' SERVICE_SPOTIFY_FOLLOW_ARTISTS:str = 'follow_artists' SERVICE_SPOTIFY_FOLLOW_PLAYLIST:str = 'follow_playlist' SERVICE_SPOTIFY_FOLLOW_USERS:str = 'follow_users' @@ -111,6 +117,7 @@ SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE:str = 'get_spotify_connect_device' SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES:str = 'get_spotify_connect_devices' SERVICE_SPOTIFY_GET_TRACK_FAVORITES:str = 'get_track_favorites' +SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES:str = 'get_tracks_audio_features' SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS:str = 'get_users_top_artists' SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS:str = 'get_users_top_tracks' SERVICE_SPOTIFY_PLAYER_ACTIVATE_DEVICES:str = 'player_activate_devices' @@ -154,6 +161,47 @@ SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES:str = 'zeroconf_discover_devices' +SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) + +SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) + +SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) + +SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) + +SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) + +SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Optional("ids"): cv.string, + } +) SERVICE_SPOTIFY_FOLLOW_ARTISTS_SCHEMA = vol.Schema( { @@ -165,7 +213,7 @@ SERVICE_SPOTIFY_FOLLOW_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("playlist_id"): cv.string, + vol.Optional("playlist_id"): cv.string, vol.Optional("public"): cv.boolean, } ) @@ -180,7 +228,7 @@ SERVICE_SPOTIFY_GET_ALBUM_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("album_id"): cv.string, + vol.Optional("album_id"): cv.string, vol.Optional("market"): cv.string, } ) @@ -188,7 +236,7 @@ SERVICE_SPOTIFY_GET_ALBUM_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -199,7 +247,7 @@ SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("country"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -210,16 +258,16 @@ SERVICE_SPOTIFY_GET_ARTIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("artist_id"): cv.string, + vol.Optional("artist_id"): cv.string, } ) SERVICE_SPOTIFY_GET_ARTIST_ALBUMS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("artist_id"): cv.string, + vol.Optional("artist_id"): cv.string, vol.Optional("include_groups"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -231,7 +279,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Optional("after"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } @@ -250,7 +298,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("category_id"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("country"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -261,7 +309,7 @@ SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("country"): cv.string, vol.Optional("locale"): cv.string, @@ -304,7 +352,7 @@ SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("after", default=0): vol.All(vol.Range(min=0,max=99999999999999)), vol.Optional("before", default=0): vol.All(vol.Range(min=0,max=99999999999999)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -314,7 +362,7 @@ SERVICE_SPOTIFY_GET_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("playlist_id"): cv.string, + vol.Optional("playlist_id"): cv.string, vol.Optional("market"): cv.string, vol.Optional("fields"): cv.string, vol.Optional("additional_types"): cv.string, @@ -324,7 +372,7 @@ SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, @@ -334,7 +382,7 @@ SERVICE_SPOTIFY_GET_SHOW_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("show_id"): cv.string, + vol.Optional("show_id"): cv.string, vol.Optional("market"): cv.string, } ) @@ -342,8 +390,8 @@ SERVICE_SPOTIFY_GET_SHOW_EPISODES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("show_id"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("show_id"): cv.string, + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -353,7 +401,7 @@ SERVICE_SPOTIFY_GET_SHOW_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, @@ -383,7 +431,7 @@ SERVICE_SPOTIFY_GET_TRACK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), @@ -391,11 +439,18 @@ } ) +SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES_SCHEMA = vol.Schema( + { + vol.Required("entity_id"): cv.entity_id, + vol.Required("ids"): cv.string, + } +) + SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("time_range"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, @@ -406,7 +461,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Optional("time_range"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, @@ -631,7 +686,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -643,7 +698,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -655,7 +710,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -667,7 +722,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -679,7 +734,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -691,7 +746,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -703,7 +758,7 @@ { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, - vol.Optional("limit", default=50): vol.All(vol.Range(min=1,max=50)), + vol.Optional("limit", default=50): vol.All(vol.Range(min=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, @@ -721,7 +776,7 @@ SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, - vol.Required("playlist_id"): cv.string, + vol.Optional("playlist_id"): cv.string, } ) @@ -1146,7 +1201,49 @@ async def service_handle_spotify_serviceresponse(service: ServiceCall) -> Servic response:dict = {} # process service request. - if service.service == SERVICE_SPOTIFY_GET_ALBUM: + if service.service == SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES: + + # check album favorites. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_album_favorites, ids) + + elif service.service == SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING: + + # check artists following. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_artists_following, ids) + + elif service.service == SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES: + + # check audiobook favorites. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_audiobook_favorites, ids) + + elif service.service == SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES: + + # check episode favorites. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_episode_favorites, ids) + + elif service.service == SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES: + + # check show favorites. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_show_favorites, ids) + + elif service.service == SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES: + + # check track favorites. + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_check_track_favorites, ids) + + elif service.service == SERVICE_SPOTIFY_GET_ALBUM: # get spotify album. album_id = service.data.get("album_id") @@ -1360,6 +1457,14 @@ async def service_handle_spotify_serviceresponse(service: ServiceCall) -> Servic _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_track_favorites, limit, offset, market, limit_total, sort_result) + elif service.service == SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES: + + # get spotify album favorites. + limit = service.data.get("limit") + ids = service.data.get("ids") + _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) + response = await hass.async_add_executor_job(entity.service_spotify_get_tracks_audio_features, ids) + elif service.service == SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS: # get spotify users top artists. @@ -1613,6 +1718,60 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id: # register all services this component provides, and their corresponding schemas. + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES, SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING, SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES, SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES, SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES, SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_FOLLOW_ARTISTS, SERVICE_SPOTIFY_FOLLOW_ARTISTS_SCHEMA) hass.services.async_register( DOMAIN, @@ -1838,6 +1997,15 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id: supports_response=SupportsResponse.ONLY, ) + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES, SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES_SCHEMA) + hass.services.async_register( + DOMAIN, + SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES, + service_handle_spotify_serviceresponse, + schema=SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES_SCHEMA, + supports_response=SupportsResponse.ONLY, + ) + _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS, SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS_SCHEMA) hass.services.async_register( DOMAIN, diff --git a/custom_components/spotifyplus/manifest.json b/custom_components/spotifyplus/manifest.json index c2809ce..5277515 100644 --- a/custom_components/spotifyplus/manifest.json +++ b/custom_components/spotifyplus/manifest.json @@ -17,10 +17,10 @@ "requests>=2.31.0", "requests_oauthlib>=1.3.1", "smartinspectPython>=3.0.33", - "spotifywebapiPython>=1.0.88", + "spotifywebapiPython>=1.0.95", "urllib3>=1.21.1,<1.27", "zeroconf>=0.132.2" ], - "version": "1.0.54", + "version": "1.0.55", "zeroconf": [ "_spotify-connect._tcp.local." ] } diff --git a/custom_components/spotifyplus/media_player.py b/custom_components/spotifyplus/media_player.py index 01423f6..88533ea 100644 --- a/custom_components/spotifyplus/media_player.py +++ b/custom_components/spotifyplus/media_player.py @@ -34,6 +34,7 @@ AlbumSimplified, Artist, ArtistPage, + AudioFeatures, Category, CategoryPage, Context, @@ -122,8 +123,21 @@ # our extra state attribute names. -ATTR_SPOTIFYPLUS_DEVICE_ID = "spotifyplus_device_id" -ATTR_SPOTIFYPLUS_DEVICE_NAME = "spotifyplus_device_name" +ATTR_SPOTIFYPLUS_CONTEXT_URI = "sp_context_uri" +ATTR_SPOTIFYPLUS_DEVICE_ID = "sp_device_id" +ATTR_SPOTIFYPLUS_DEVICE_NAME = "sp_device_name" +ATTR_SPOTIFYPLUS_PLAYLIST_NAME = "sp_playlist_name" +ATTR_SPOTIFYPLUS_PLAYLIST_URI = "sp_playlist_uri" +ATTR_SPOTIFYPLUS_USER_COUNTRY = "sp_user_country" +ATTR_SPOTIFYPLUS_USER_DISPLAY_NAME = "sp_user_display_name" +ATTR_SPOTIFYPLUS_USER_EMAIL = "sp_user_email" +ATTR_SPOTIFYPLUS_USER_ID = "sp_user_id" +ATTR_SPOTIFYPLUS_USER_PRODUCT = "sp_user_product" +ATTR_SPOTIFYPLUS_USER_URI = "sp_user_uri" + +ATTRVALUE_NO_DEVICE = "no_device" +ATTRVALUE_UNKNOWN = "unknown" + # annotate the `spotify_exception_handler` callable. @@ -312,7 +326,7 @@ def __init__(self, data:InstanceDataSpotifyPlus) -> None: self._playerState:PlayerPlayState = PlayerPlayState() self._spotifyConnectDevice:SpotifyConnectDevice = None self._sonosDevice:SoCo = None - + # initialize base class attributes (MediaPlayerEntity). self._attr_icon = "mdi:spotify" self._attr_media_image_remotely_accessible = False @@ -401,8 +415,19 @@ 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] = "no_device" - attributes[ATTR_SPOTIFYPLUS_DEVICE_NAME] = "no_device" + # attributes[ATTR_SPOTIFYPLUS_CONTEXT_URI] = ATTRVALUE_UNKNOWN + attributes[ATTR_SPOTIFYPLUS_DEVICE_ID] = ATTRVALUE_NO_DEVICE + attributes[ATTR_SPOTIFYPLUS_DEVICE_NAME] = ATTRVALUE_NO_DEVICE + # attributes[ATTR_SPOTIFYPLUS_PLAYLIST_NAME] = ATTRVALUE_UNKNOWN + # attributes[ATTR_SPOTIFYPLUS_PLAYLIST_URI] = 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_ID] = ATTRVALUE_UNKNOWN + attributes[ATTR_SPOTIFYPLUS_USER_PRODUCT] = ATTRVALUE_UNKNOWN + attributes[ATTR_SPOTIFYPLUS_USER_URI] = ATTRVALUE_UNKNOWN + + self.data.spotifyClient.UserProfile.DisplayName # get currently active device id. if self._playerState is not None: @@ -410,12 +435,26 @@ def extra_state_attributes(self) -> dict: attributes[ATTR_SPOTIFYPLUS_DEVICE_ID] = self._playerState.Device.Id attributes[ATTR_SPOTIFYPLUS_DEVICE_NAME] = self._playerState.Device.Name 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 # 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 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 + return attributes @@ -1667,9 +1706,304 @@ def _GetDefaultDeviceOption( return deviceId - def service_spotify_follow_artists(self, - ids:str=None, - ) -> None: + 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: 50 IDs. + Example: `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX` + If null, the currently playing track album uri id value is used. + """ + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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: 50 IDs. + Example: `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk` + If null, the currently playing track artist uri id value is used. + """ + 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) + + # follow artist(s). + _logsi.LogVerbose("Adding items(s) to Spotify Artist Favorites") + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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: 50 IDs. + Example: `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe` + If null, the currently playing audiobook uri id value is used. + """ + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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: 50 IDs. + Example: `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4` + If null, the currently playing episode uri id value is used. + """ + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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: 50 IDs. + Example: `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk` + If null, the currently playing show uri id value is used. + """ + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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: 50 IDs. + Example: `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK` + If null, the currently playing context uri id value is used. + """ + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(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. @@ -1707,10 +2041,11 @@ def service_spotify_follow_artists(self, _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_follow_playlist(self, - playlistId:str=None, - public:bool=True, - ) -> None: + def service_spotify_follow_playlist( + self, + playlistId:str=None, + public:bool=True, + ) -> None: """ Add the current user as a follower of a playlist. @@ -1718,6 +2053,7 @@ def service_spotify_follow_playlist(self, 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. @@ -1790,10 +2126,11 @@ def service_spotify_follow_users(self, _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_get_album(self, - albumId:str, - market:str=None, - ) -> dict: + def service_spotify_get_album( + self, + albumId:str=None, + market:str=None, + ) -> dict: """ Get Spotify catalog information for a single album. @@ -1801,6 +2138,7 @@ def service_spotify_get_album(self, 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 @@ -2006,16 +2344,18 @@ def service_spotify_get_album_new_releases( _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_get_artist(self, - artistId:str, - ) -> dict: + 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. - Example: `6APm8EjxOHSYM5B4i3vT3q` + If null, the currently playing artist uri id value is used. + Example: `6APm8EjxOHSYM5B4i3vT3q` Returns: A dictionary that contains the following keys: @@ -2072,7 +2412,8 @@ def service_spotify_get_artist_albums( Args: artistId (str): The Spotify ID of the artist. - Example: `6APm8EjxOHSYM5B4i3vT3q` + 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. @@ -2121,6 +2462,10 @@ def service_spotify_get_artist_albums( 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) @@ -2812,19 +3157,21 @@ def service_spotify_get_player_recent_tracks(self, _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_get_playlist(self, - playlistId:str, - market:str=None, - fields:str=None, - additionalTypes:str=None - ) -> dict: + 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. - Example: `5v5ETK9WFXAnGQ3MRubKuE` + 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 @@ -2950,17 +3297,19 @@ def service_spotify_get_playlist_favorites( _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_get_show(self, - showId:str, - market:str=None, - ) -> dict: + 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. - Example: `5CfCWKI5pZ28U0uOzXkDHe` + 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 @@ -3008,20 +3357,22 @@ def service_spotify_get_show(self, _logsi.LeaveMethod(SILevel.Debug, apiMethodName) - def service_spotify_get_show_episodes(self, - showId:str, - limit:int, - offset:int, - market:str=None, - limitTotal:int=None - ) -> dict: + 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. - Example: `6kAsbP8pxwaU2kPibKTuHE` + 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. @@ -3392,6 +3743,64 @@ def service_spotify_get_track_favorites( _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_track_favorites' + apiMethodParms:SIMethodParmListContext = None + result:TrackPageSaved = TrackPageSaved() + + 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 HomeAssistantError(ex.Message) + except SpotifyWebApiError as ex: + raise HomeAssistantError(ex.Message) + + finally: + + # trace. + _logsi.LeaveMethod(SILevel.Debug, apiMethodName) + + def service_spotify_get_users_top_artists( self, timeRange:str, @@ -6163,6 +6572,7 @@ def service_spotify_unfollow_playlist(self, 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 diff --git a/custom_components/spotifyplus/services.yaml b/custom_components/spotifyplus/services.yaml index a82b244..1cf1ed4 100644 --- a/custom_components/spotifyplus/services.yaml +++ b/custom_components/spotifyplus/services.yaml @@ -1,3 +1,129 @@ +check_album_favorites: + name: Check Album Favorites + description: Check if one or more albums (or the currently playing album) exists in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify album id's (e.g. `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`). A maximum of 50 id's may be specified. If omitted, the currently playing track album uri id value is used. + example: "6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX" + required: false + selector: + text: + +check_artists_following: + name: Check Artists Following + description: Check if one or more artists (or the currently playing artists) is followed in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify artist id's (e.g. `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`). A maximum of 50 id's may be specified. If omitted, the currently playing track artist uri id value is used. + example: "2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk" + required: false + selector: + text: + +check_audiobook_favorites: + name: Check Audiobook Favorites + description: Check if one or more audiobooks (or the currently playing audiobook) exists in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify audiobook id's (e.g. `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`). A maximum of 50 id's may be specified. If omitted, the currently playing audiobook uri id value is used. + example: "3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe" + required: false + selector: + text: + +check_episode_favorites: + name: Check Episode Favorites + description: Check if one or more episodes (or the currently playing episode) exists in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify episode id's (e.g. `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`). A maximum of 50 id's may be specified. If omitted, the currently playing episode uri id value is used. + example: "3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4" + required: false + selector: + text: + +check_show_favorites: + name: Check Show Favorites + description: Check if one or more shows (or the currently playing show) exists in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify show id's (e.g. `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`). A maximum of 50 id's may be specified. If omitted, the currently playing show uri id value is used. + example: "6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk" + required: false + selector: + text: + +check_track_favorites: + name: Check Track Favorites + description: Check if one or more tracks (or the currently playing track) exists in the current user's 'Your Library' favorites. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of Spotify track id's (e.g. `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`). A maximum of 50 id's may be specified. If omitted, the currently playing context uri id value is used. + example: "1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK" + required: false + selector: + text: + follow_artists: name: Follow Artists description: Add the current user as a follower of one or more artists. @@ -34,9 +160,9 @@ follow_playlist: domain: media_player playlist_id: name: Playlist ID - description: The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). + description: The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used. example: "3cEYpjA9oz9GiPac4AsH4n" - required: true + required: false selector: text: public: @@ -83,9 +209,9 @@ get_album: domain: media_player album_id: name: Album Id - description: The Spotify ID of the album. + description: The Spotify ID of the album. If omitted, the currently playing album uri id value is used. example: "6vc9OTcyd3hyzabCmsdnwE" - required: true + required: false selector: text: market: @@ -223,9 +349,9 @@ get_artist: domain: media_player artist_id: name: Artist Id - description: The Spotify ID of the artist. + description: The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used. example: "6APm8EjxOHSYM5B4i3vT3q" - required: true + required: false selector: text: @@ -244,16 +370,16 @@ get_artist_albums: domain: media_player artist_id: name: Artist Id - description: The Spotify ID of the artist. + description: The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used. example: "6APm8EjxOHSYM5B4i3vT3q" - required: true + required: false selector: text: include_groups: name: Include Groups - description: 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`. + description: A comma-separated list of keywords that will be used to filter the response. If not supplied, only `album` types will be returned. Valid values are `album`, `single`, `appears_on`, `compilation`. example: "album" - required: true + required: false selector: text: limit: @@ -678,9 +804,9 @@ get_playlist: domain: media_player playlist_id: name: Playlist Id - description: The Spotify ID of the playlist. + description: The Spotify ID of the playlist. If omitted, the currently playing playlist uri id value is used. example: "5v5ETK9WFXAnGQ3MRubKuE" - required: true + required: false selector: text: market: @@ -769,9 +895,9 @@ get_show: domain: media_player show_id: name: Show Id - description: The Spotify ID for the show. + description: The Spotify ID for the show. If omitted, the currently playing show uri id value is used. example: "5CfCWKI5pZ28U0uOzXkDHe" - required: true + required: false selector: text: market: @@ -797,9 +923,9 @@ get_show_episodes: domain: media_player show_id: name: Show Id - description: The Spotify ID for the show. + description: The Spotify ID for the show. If omitted, the currently playing show uri id value is used. example: "6kAsbP8pxwaU2kPibKTuHE" - required: true + required: false selector: text: limit: @@ -1033,6 +1159,27 @@ get_track_favorites: selector: boolean: +get_tracks_audio_features: + name: Get Tracks Audio Features + description: Get audio features for multiple tracks based on their Spotify IDs. + fields: + entity_id: + name: Entity ID + description: Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API. + example: "media_player.spotifyplus_username" + required: true + selector: + entity: + integration: spotifyplus + domain: media_player + ids: + name: ID's + description: A comma-separated list of the Spotify track IDs. Maximum of 100 IDs. Example = `7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ,2takcwOaAZWiXQijPHIx7B`. + example: "7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ,2takcwOaAZWiXQijPHIx7B" + required: true + selector: + text: + get_users_top_artists: name: Get Users Top Artists description: Get the current user's top artists based on calculated affinity. @@ -2468,9 +2615,9 @@ unfollow_playlist: domain: media_player playlist_id: name: Playlist ID - description: The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). + description: The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used. example: "3cEYpjA9oz9GiPac4AsH4n" - required: true + required: false selector: text: diff --git a/custom_components/spotifyplus/strings.json b/custom_components/spotifyplus/strings.json index 57031a2..462de79 100644 --- a/custom_components/spotifyplus/strings.json +++ b/custom_components/spotifyplus/strings.json @@ -62,6 +62,90 @@ } }, "services": { + "check_album_favorites": { + "name": "Check Album Favorites", + "description": "Check if one or more albums (or the currently playing album) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify album id's (e.g. `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`). A maximum of 50 id's may be specified. If omitted, the currently playing track album uri id value is used." + } + } + }, + "check_artists_following": { + "name": "Check Artists Following", + "description": "Check if one or more artists (or the currently playing artists) is followed in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify artist id's (e.g. `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`). A maximum of 50 id's may be specified. If omitted, the currently playing track artist uri id value is used." + } + } + }, + "check_audiobook_favorites": { + "name": "Check Audiobook Favorites", + "description": "Check if one or more audiobooks (or the currently playing audiobook) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify audiobook id's (e.g. `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`). A maximum of 50 id's may be specified. If omitted, the currently playing audiobook uri id value is used." + } + } + }, + "check_episode_favorites": { + "name": "Check Episode Favorites", + "description": "Check if one or more episodes (or the currently playing episode) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify episode id's (e.g. `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`). A maximum of 50 id's may be specified. If omitted, the currently playing episode uri id value is used." + } + } + }, + "check_show_favorites": { + "name": "Check Show Favorites", + "description": "Check if one or more shows (or the currently playing show) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify show id's (e.g. `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`). A maximum of 50 id's may be specified. If omitted, the currently playing show uri id value is used." + } + } + }, + "check_track_favorites": { + "name": "Check Track Favorites", + "description": "Check if one or more tracks (or the currently playing track) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify track id's (e.g. `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`). A maximum of 50 id's may be specified. If omitted, the currently playing context uri id value is used." + } + } + }, "follow_artists": { "name": "Follow Artists", "description": "Add the current user as a follower of one or more artists.", @@ -86,7 +170,7 @@ }, "playlist_id": { "name": "ID's", - "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`)." + "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used." }, "public": { "name": "Public?", @@ -118,7 +202,7 @@ }, "album_id": { "name": "Album Id", - "description": "The Spotify ID of the album." + "description": "The Spotify ID of the album. If omitted, the currently playing album uri id value is used." }, "market": { "name": "Market / Country Code", @@ -196,7 +280,7 @@ }, "artist_id": { "name": "Artist Id", - "description": "The Spotify ID of the artist." + "description": "The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used." } } }, @@ -210,11 +294,11 @@ }, "artist_id": { "name": "Artist Id", - "description": "The Spotify ID of the artist." + "description": "The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used." }, "include_groups": { "name": "Include Groups", - "description": "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`." + "description": "A comma-separated list of keywords that will be used to filter the response. If not supplied, only `album` types will be returned. Valid values are `album`, `single`, `appears_on`, `compilation`." }, "limit": { "name": "Limit", @@ -458,7 +542,7 @@ }, "playlist_id": { "name": "Playlist Id", - "description": "The Spotify ID of the playlist." + "description": "The Spotify ID of the playlist. If omitted, the currently playing playlist uri id value is used." }, "market": { "name": "Market / Country Code", @@ -510,7 +594,7 @@ }, "show_id": { "name": "Show Id", - "description": "The Spotify ID of the show." + "description": "The Spotify ID of the show. If omitted, the currently playing show uri id value is used." }, "market": { "name": "Market / Country Code", @@ -528,7 +612,7 @@ }, "show_id": { "name": "Show ID", - "description": "The Spotify ID for the show." + "description": "The Spotify ID for the show. If omitted, the currently playing show uri id value is used." }, "limit": { "name": "Limit", @@ -656,6 +740,20 @@ } } }, + "get_tracks_audio_features": { + "name": "Get Tracks Audio Features", + "description": "Get audio features for multiple tracks based on their Spotify IDs.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of the Spotify track IDs. Maximum of 100 IDs. Example `7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ,2takcwOaAZWiXQijPHIx7B`." + } + } + }, "get_users_top_artists": { "name": "Get Users Top Artists", "description": "Get the current user's top artists based on calculated affinity.", @@ -1484,7 +1582,7 @@ }, "playlist_id": { "name": "Playlist ID", - "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`)." + "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used." } } }, diff --git a/custom_components/spotifyplus/translations/en.json b/custom_components/spotifyplus/translations/en.json index 57031a2..462de79 100644 --- a/custom_components/spotifyplus/translations/en.json +++ b/custom_components/spotifyplus/translations/en.json @@ -62,6 +62,90 @@ } }, "services": { + "check_album_favorites": { + "name": "Check Album Favorites", + "description": "Check if one or more albums (or the currently playing album) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify album id's (e.g. `6vc9OTcyd3hyzabCmsdnwE,382ObEPsp2rxGrnsizN5TX`). A maximum of 50 id's may be specified. If omitted, the currently playing track album uri id value is used." + } + } + }, + "check_artists_following": { + "name": "Check Artists Following", + "description": "Check if one or more artists (or the currently playing artists) is followed in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify artist id's (e.g. `2CIMQHirSU0MQqyYHq0eOx,1IQ2e1buppatiN1bxUVkrk`). A maximum of 50 id's may be specified. If omitted, the currently playing track artist uri id value is used." + } + } + }, + "check_audiobook_favorites": { + "name": "Check Audiobook Favorites", + "description": "Check if one or more audiobooks (or the currently playing audiobook) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify audiobook id's (e.g. `3PFyizE2tGCSRLusl2Qizf,7iHfbu1YPACw6oZPAFJtqe`). A maximum of 50 id's may be specified. If omitted, the currently playing audiobook uri id value is used." + } + } + }, + "check_episode_favorites": { + "name": "Check Episode Favorites", + "description": "Check if one or more episodes (or the currently playing episode) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify episode id's (e.g. `3F97boSWlXi8OzuhWClZHQ,1hPX5WJY6ja6yopgVPBqm4`). A maximum of 50 id's may be specified. If omitted, the currently playing episode uri id value is used." + } + } + }, + "check_show_favorites": { + "name": "Check Show Favorites", + "description": "Check if one or more shows (or the currently playing show) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify show id's (e.g. `6kAsbP8pxwaU2kPibKTuHE,4rOoJ6Egrf8K2IrywzwOMk`). A maximum of 50 id's may be specified. If omitted, the currently playing show uri id value is used." + } + } + }, + "check_track_favorites": { + "name": "Check Track Favorites", + "description": "Check if one or more tracks (or the currently playing track) exists in the current user's 'Your Library' favorites.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of Spotify track id's (e.g. `1kWUud3vY5ij5r62zxpTRy,4eoYKv2kDwJS7gRGh5q6SK`). A maximum of 50 id's may be specified. If omitted, the currently playing context uri id value is used." + } + } + }, "follow_artists": { "name": "Follow Artists", "description": "Add the current user as a follower of one or more artists.", @@ -86,7 +170,7 @@ }, "playlist_id": { "name": "ID's", - "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`)." + "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used." }, "public": { "name": "Public?", @@ -118,7 +202,7 @@ }, "album_id": { "name": "Album Id", - "description": "The Spotify ID of the album." + "description": "The Spotify ID of the album. If omitted, the currently playing album uri id value is used." }, "market": { "name": "Market / Country Code", @@ -196,7 +280,7 @@ }, "artist_id": { "name": "Artist Id", - "description": "The Spotify ID of the artist." + "description": "The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used." } } }, @@ -210,11 +294,11 @@ }, "artist_id": { "name": "Artist Id", - "description": "The Spotify ID of the artist." + "description": "The Spotify ID of the artist. If omitted, the currently playing artist uri id value is used." }, "include_groups": { "name": "Include Groups", - "description": "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`." + "description": "A comma-separated list of keywords that will be used to filter the response. If not supplied, only `album` types will be returned. Valid values are `album`, `single`, `appears_on`, `compilation`." }, "limit": { "name": "Limit", @@ -458,7 +542,7 @@ }, "playlist_id": { "name": "Playlist Id", - "description": "The Spotify ID of the playlist." + "description": "The Spotify ID of the playlist. If omitted, the currently playing playlist uri id value is used." }, "market": { "name": "Market / Country Code", @@ -510,7 +594,7 @@ }, "show_id": { "name": "Show Id", - "description": "The Spotify ID of the show." + "description": "The Spotify ID of the show. If omitted, the currently playing show uri id value is used." }, "market": { "name": "Market / Country Code", @@ -528,7 +612,7 @@ }, "show_id": { "name": "Show ID", - "description": "The Spotify ID for the show." + "description": "The Spotify ID for the show. If omitted, the currently playing show uri id value is used." }, "limit": { "name": "Limit", @@ -656,6 +740,20 @@ } } }, + "get_tracks_audio_features": { + "name": "Get Tracks Audio Features", + "description": "Get audio features for multiple tracks based on their Spotify IDs.", + "fields": { + "entity_id": { + "name": "Entity ID", + "description": "Entity ID of the SpotifyPlus device that will make the request to the Spotify Web API." + }, + "ids": { + "name": "ID's", + "description": "A comma-separated list of the Spotify track IDs. Maximum of 100 IDs. Example `7ouMYWpwJ422jRcDASZB7P,4VqPOruhp5EdPBeR92t6lQ,2takcwOaAZWiXQijPHIx7B`." + } + } + }, "get_users_top_artists": { "name": "Get Users Top Artists", "description": "Get the current user's top artists based on calculated affinity.", @@ -1484,7 +1582,7 @@ }, "playlist_id": { "name": "Playlist ID", - "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`)." + "description": "The Spotify ID of the playlist (e.g. `3cEYpjA9oz9GiPac4AsH4n`). If omitted, the currently playing playlist uri id value is used." } } }, diff --git a/hacs.json b/hacs.json index bba113b..01be5ae 100644 --- a/hacs.json +++ b/hacs.json @@ -1,8 +1,9 @@ { - "name": "SpotifyPlus", - "filename": "spotifyplus.zip", - "hide_default_branch": true, - "homeassistant": "2023.8.0", - "render_readme": true, - "zip_release": true + "name": "SpotifyPlus", + "filename": "spotifyplus.zip", + "hide_default_branch": true, + "homeassistant": "2023.8.0", + "render_readme": true, + "zip_release": true, + "persistent_directory": "data" } diff --git a/requirements.txt b/requirements.txt index 50051ed..3192041 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ colorlog==6.7.0 homeassistant==2024.5.0 ruff==0.1.3 smartinspectPython>=3.0.33 -spotifywebapiPython>=1.0.88 +spotifywebapiPython>=1.0.95