""" The spotifyplus integration. """ from __future__ import annotations from asyncio import run_coroutine_threadsafe from urllib3._version import __version__ as urllib3_version import functools import json import logging import os import threading import voluptuous as vol from spotifywebapipython import SpotifyClient, SpotifyAuthToken from spotifywebapipython.models import SpotifyConnectDevices, SpotifyConnectDevice from spotifywebapipython.const import VERSION as spotifywebapipython_VERSION from homeassistant.components import zeroconf from homeassistant.components.media_player import MediaPlayerEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform, CONF_ID, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant, ServiceCall, ServiceResponse, SupportsResponse from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady, HomeAssistantError, IntegrationError, ServiceValidationError from homeassistant.helpers import config_validation as cv, entity_registry as er from homeassistant.helpers.config_entry_oauth2_flow import (OAuth2Session, async_get_config_entry_implementation) from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType from .appmessages import STAppMessages from .instancedata_spotifyplus import InstanceDataSpotifyPlus from .const import ( CONF_OPTION_DEVICE_LOGINID, CONF_OPTION_DEVICE_PASSWORD, CONF_OPTION_DEVICE_USERNAME, DOMAIN, SPOTIFY_SCOPES, SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS, SERVICE_SPOTIFY_CHECK_ALBUM_FAVORITES, SERVICE_SPOTIFY_CHECK_ARTISTS_FOLLOWING, SERVICE_SPOTIFY_CHECK_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_CHECK_EPISODE_FAVORITES, SERVICE_SPOTIFY_CHECK_PLAYLIST_FOLLOWERS, SERVICE_SPOTIFY_CHECK_SHOW_FAVORITES, SERVICE_SPOTIFY_CHECK_TRACK_FAVORITES, SERVICE_SPOTIFY_CHECK_USER_FAVORITES, SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING, SERVICE_SPOTIFY_FOLLOW_ARTISTS, SERVICE_SPOTIFY_FOLLOW_PLAYLIST, SERVICE_SPOTIFY_FOLLOW_USERS, SERVICE_SPOTIFY_GET_ALBUM, SERVICE_SPOTIFY_GET_ALBUM_FAVORITES, SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES, SERVICE_SPOTIFY_GET_ALBUM_TRACKS, SERVICE_SPOTIFY_GET_ARTIST, SERVICE_SPOTIFY_GET_ARTIST_ALBUMS, SERVICE_SPOTIFY_GET_ARTIST_INFO, SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS, SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS, SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED, SERVICE_SPOTIFY_GET_AUDIOBOOK, SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS, SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST, SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS, SERVICE_SPOTIFY_GET_CHAPTER, SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE, SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE, SERVICE_SPOTIFY_GET_EPISODE, SERVICE_SPOTIFY_GET_EPISODE_FAVORITES, SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS, SERVICE_SPOTIFY_GET_ID_FROM_URI, SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS, SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS, SERVICE_SPOTIFY_GET_PLAYER_DEVICES, SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO, SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING, SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE, SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO, SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS, SERVICE_SPOTIFY_GET_PLAYLIST, SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE, SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES, SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS, SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER, SERVICE_SPOTIFY_GET_SHOW, SERVICE_SPOTIFY_GET_SHOW_EPISODES, SERVICE_SPOTIFY_GET_SHOW_FAVORITES, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES, SERVICE_SPOTIFY_GET_TRACK, SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES, SERVICE_SPOTIFY_GET_TRACK_FAVORITES, SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS, SERVICE_SPOTIFY_GET_TRACKS_AUDIO_FEATURES, SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS, SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS, SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS, SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME, SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS, SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE, SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE, SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL, SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK, SERVICE_SPOTIFY_PLAYLIST_CHANGE, SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD, SERVICE_SPOTIFY_PLAYLIST_CREATE, SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD, SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE, SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES, SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES, SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES, SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES, SERVICE_SPOTIFY_REMOVE_USER_FAVORITES, SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES, SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES, SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES, SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES, SERVICE_SPOTIFY_SAVE_USER_FAVORITES, SERVICE_SPOTIFY_SEARCH_ALL, SERVICE_SPOTIFY_SEARCH_ALBUMS, SERVICE_SPOTIFY_SEARCH_ARTISTS, SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS, SERVICE_SPOTIFY_SEARCH_EPISODES, SERVICE_SPOTIFY_SEARCH_PLAYLISTS, SERVICE_SPOTIFY_SEARCH_SHOWS, SERVICE_SPOTIFY_SEARCH_TRACKS, SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL, SERVICE_SPOTIFY_UNFOLLOW_ARTISTS, SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST, SERVICE_SPOTIFY_UNFOLLOW_USERS, SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT, SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT, SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO, SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES, SERVICE_VOLUME_SET_STEP, SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS, SERVICE_TEST_TOKEN_EXPIRE, ) __all__ = [ "DOMAIN", ] _LOGGER = logging.getLogger(__name__) TOKEN_STATUS:str = 'status' TOKEN_STATUS_REFRESH_EVENT:str = 'TokenRefreshEvent' TOKENUPDATER_LOCK = threading.Lock() # syncronous lock to sync access to token updates. REAUTH_TEST_FIRST_TIME:bool = None # used when testing app creds reauth processing try: from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIConfigurationTimer, SIColors # load SmartInspect settings from a configuration settings file; # configuration file will be monitored for changes, and reloaded if required. siConfigPath: str = "./smartinspect.cfg" SIAuto.Si.LoadConfiguration(siConfigPath) # get smartinspect logger reference; create a new session for this module name. _logsi:SISession = SIAuto.Si.GetSession(__name__) if (_logsi == None): _logsi = SIAuto.Si.AddSession(__name__, True) _logsi.SystemLogger = _LOGGER _logsi.LogSeparator(SILevel.Error) _logsi.LogVerbose("__init__.py HAS SpotifyPlus: initialization") _logsi.LogAppDomain(SILevel.Verbose) _logsi.LogSystem(SILevel.Verbose) except Exception as ex: _LOGGER.warning("HAS SpotifyPlus could not init SmartInspect debugging! %s", str(ex)) PLATFORMS = [Platform.MEDIA_PLAYER] """ List of platforms to support. There should be a matching .py file for each (e.g. "media_player") """ CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) """ Configuration schema. """ # ----------------------------------------------------------------------------------- # Custom Service schemas. # ----------------------------------------------------------------------------------- SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("uris"): cv.string, vol.Optional("device_id"): cv.string, vol.Optional("verify_device_id"): cv.boolean, vol.Optional("delay", default=0.15): vol.All(vol.Range(min=0,max=10.0)), } ) 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_PLAYLIST_FOLLOWERS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, vol.Optional("user_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_CHECK_USER_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("uris"): cv.string, } ) SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("ids"): cv.string, } ) SERVICE_SPOTIFY_FOLLOW_ARTISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_FOLLOW_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, vol.Optional("public"): cv.boolean, } ) SERVICE_SPOTIFY_FOLLOW_USERS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("ids"): cv.string, } ) SERVICE_SPOTIFY_GET_ALBUM_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("album_id"): cv.string, vol.Optional("market"): cv.string, } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("country"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_ALBUM_TRACKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("album_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=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_GET_ARTIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("artist_id"): cv.string, } ) SERVICE_SPOTIFY_GET_ARTIST_ALBUMS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("artist_id"): cv.string, vol.Optional("include_groups"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_ARTIST_INFO_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("artist_id"): cv.string, } ) SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("artist_id"): cv.string, vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("artist_id"): cv.string, vol.Optional("market"): cv.string, vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("after"): cv.string, 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, } ) SERVICE_SPOTIFY_GET_AUDIOBOOK_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("audiobook_id"): cv.string, vol.Optional("market"): cv.string, } ) SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("audiobook_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=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, 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=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("country"): cv.string, vol.Optional("locale"): cv.string, vol.Optional("refresh"): cv.boolean, } ) SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("category_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=10000)), vol.Optional("country"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_CHAPTER_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("chapter_id"): cv.string, vol.Optional("market"): cv.string, } ) SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("image_url"): cv.string, vol.Required("output_path"): cv.string, } ) SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, } ) SERVICE_SPOTIFY_GET_EPISODE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("episode_id"): cv.string, vol.Optional("market"): cv.string, } ) SERVICE_SPOTIFY_GET_EPISODE_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, 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=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("country"): cv.string, vol.Optional("locale"): cv.string, vol.Optional("timestamp"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_ID_FROM_URI_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("uri"): cv.string, } ) SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("image_source"): cv.string, vol.Optional("color_count", default=10): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=1, max=256))), vol.Optional("color_quality", default=None): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=1, max=10))), vol.Optional("brightness_filter_low", default=None): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=0, max=765))), vol.Optional("brightness_filter_high", default=None): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=0, max=765))), vol.Optional("hue_distance_filter", default=None): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=0, max=360))), } ) SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("image_source"): cv.string, vol.Optional("color_count", default=64): vol.All(vol.Range(min=1,max=256)), vol.Optional("color_quality", default=1): vol.All(vol.Range(min=1,max=10)), } ) SERVICE_SPOTIFY_GET_PLAYER_DEVICES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("refresh"): cv.boolean, vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, } ) SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("market"): cv.string, vol.Optional("additional_types"): cv.string, } ) SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("market"): cv.string, vol.Optional("additional_types"): cv.string, } ) SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, } ) 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=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)), } ) SERVICE_SPOTIFY_GET_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, vol.Optional("market"): cv.string, vol.Optional("fields"): cv.string, vol.Optional("additional_types"): cv.string, } ) SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_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=10000)), vol.Optional("market"): cv.string, vol.Optional("fields"): cv.string, vol.Optional("additional_types"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("user_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=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_SHOW_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("show_id"): cv.string, vol.Optional("market"): cv.string, } ) SERVICE_SPOTIFY_GET_SHOW_EPISODES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, 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=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, vol.Optional("exclude_audiobooks"): cv.boolean, } ) SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("device_value"): cv.string, vol.Optional("verify_user_context"): cv.boolean, vol.Optional("verify_timeout", default=5.0): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("refresh_device_list"): cv.boolean, vol.Optional("activate_device"): cv.boolean, vol.Optional("delay", default=0.25): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("refresh"): cv.boolean, vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_TRACK_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("track_id"): cv.string, vol.Optional("market"): cv.string, } ) SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("track_id"): cv.string, } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("market"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, vol.Optional("filter_artist"): cv.string, vol.Optional("filter_album"): cv.string, } ) SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("limit", default=20): vol.All(vol.Range(min=0,max=50)), vol.Optional("market"): cv.string, vol.Optional("seed_artists"): cv.string, vol.Optional("seed_genres"): cv.string, vol.Optional("seed_tracks"): cv.string, vol.Optional("min_acousticness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_acousticness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_acousticness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_danceability", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_danceability", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_danceability", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_duration_ms", default=0): vol.All(vol.Range(min=0,max=999999999)), vol.Optional("max_duration_ms", default=0): vol.All(vol.Range(min=0,max=999999999)), vol.Optional("target_duration_ms", default=0): vol.All(vol.Range(min=0,max=999999999)), vol.Optional("min_energy", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_energy", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_energy", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_instrumentalness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_instrumentalness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_instrumentalness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_key", default=0): vol.All(vol.Range(min=0,max=11)), vol.Optional("max_key", default=0): vol.All(vol.Range(min=0,max=11)), vol.Optional("target_key", default=0): vol.All(vol.Range(min=0,max=11)), vol.Optional("min_liveness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_liveness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_liveness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_loudness", default=0): vol.All(vol.Range(min=-1000.0,max=1000.0)), vol.Optional("max_loudness", default=0): vol.All(vol.Range(min=-1000.0,max=1000.0)), vol.Optional("target_loudness", default=0): vol.All(vol.Range(min=-1000.0,max=1000.0)), vol.Optional("min_mode", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_mode", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_mode", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_popularity", default=0): vol.All(vol.Range(min=0,max=100)), vol.Optional("max_popularity", default=0): vol.All(vol.Range(min=0,max=100)), vol.Optional("target_popularity", default=0): vol.All(vol.Range(min=0,max=100)), vol.Optional("min_speechiness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_speechiness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_speechiness", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("min_tempo", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("max_tempo", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("target_tempo", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("min_time_signature", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("max_time_signature", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("target_time_signature", default=0): vol.All(vol.Range(min=0,max=99999)), vol.Optional("min_valence", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("max_valence", default=0): vol.All(vol.Range(min=0,max=1.0)), vol.Optional("target_valence", default=0): vol.All(vol.Range(min=-0,max=1.0)), } ) 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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS_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=0,max=50)), vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=10000)), vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), vol.Optional("sort_result"): cv.boolean, } ) SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("context_uri"): cv.string, vol.Optional("offset_uri"): cv.string, vol.Optional("offset_position", default=-1): vol.All(vol.Range(min=-1,max=11000)), vol.Optional("position_ms", default=-1): vol.All(vol.Range(min=-1,max=999999999)), vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("shuffle"): cv.boolean, vol.Optional("play_show_latest_episode"): cv.boolean, } ) SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("shuffle"): cv.boolean, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("resolve_device_id"): cv.boolean, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=999999)), vol.Optional("filter_artist"): cv.string, vol.Optional("filter_album"): cv.string, } ) SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("uris"): cv.string, vol.Optional("position_ms", default=-1): vol.All(vol.Range(min=-1,max=999999999)), vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("shuffle"): cv.boolean, } ) SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("position_ms", default=-1): vol.All(vol.Range(min=-1,max=999999999)), vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("relative_position_ms", default=0): vol.All(vol.Range(min=-999999999,max=999999999)), } ) SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("state"): cv.string, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("state"): cv.boolean, vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("volume_level", default=0): vol.All(vol.Range(min=0,max=100)), vol.Optional("device_id"): cv.string, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("device_id"): cv.string, vol.Optional("play"): cv.boolean, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), vol.Optional("refresh_device_list"): cv.boolean, vol.Optional("force_activate_device"): cv.boolean, vol.Optional("device_id_from"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_CHANGE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("playlist_id"): cv.string, vol.Required("name"): cv.string, vol.Required("description"): cv.string, vol.Required("public"): cv.boolean, vol.Required("collaborative"): cv.boolean, vol.Optional("image_path"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, vol.Required("image_path"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_CREATE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("user_id"): cv.string, vol.Required("name"): cv.string, vol.Required("description"): cv.string, vol.Required("public"): cv.boolean, vol.Required("collaborative"): cv.boolean, vol.Optional("image_path"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("playlist_id"): cv.string, vol.Optional("uris"): cv.string, vol.Optional("position"): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("playlist_id"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("playlist_id"): cv.string, vol.Optional("uris"): cv.string, vol.Optional("snapshot_id"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("playlist_id"): cv.string, vol.Required("range_start"): vol.All(vol.Range(min=0,max=99999)), vol.Required("insert_before"): vol.All(vol.Range(min=0,max=99999)), vol.Optional("range_length"): vol.All(vol.Range(min=0,max=99999)), vol.Optional("snapshot_id"): cv.string, } ) SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, vol.Optional("uris"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_REMOVE_USER_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("uris"): cv.string, } ) SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_SAVE_USER_FAVORITES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("uris"): cv.string, } ) SERVICE_SPOTIFY_SEARCH_ALL_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): cv.string, vol.Optional("criteria_type"): cv.string, vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_ALBUMS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_ARTISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_EPISODES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_PLAYLISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_SHOWS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_SEARCH_TRACKS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("criteria"): 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=10000)), vol.Optional("market"): cv.string, vol.Optional("include_external"): cv.string, vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)), } ) SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, } ) SERVICE_SPOTIFY_UNFOLLOW_ARTISTS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("ids"): cv.string, } ) SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("playlist_id"): cv.string, } ) SERVICE_SPOTIFY_UNFOLLOW_USERS_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("ids"): cv.string, } ) SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("host_ipv4_address"): cv.string, vol.Required("host_ip_port", default=8200): vol.All(vol.Range(min=1,max=65535)), vol.Required("cpath"): cv.string, vol.Optional("version"): cv.string, vol.Optional("use_ssl"): cv.boolean, vol.Optional("username"): cv.string, vol.Optional("password"): cv.string, vol.Optional("loginid"): cv.string, vol.Optional("pre_disconnect"): cv.boolean, vol.Optional("verify_device_list_entry"): cv.boolean, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("host_ipv4_address"): cv.string, vol.Required("host_ip_port", default=8200): vol.All(vol.Range(min=1,max=65535)), vol.Required("cpath"): cv.string, vol.Optional("version"): cv.string, vol.Optional("use_ssl"): cv.boolean, vol.Optional("delay", default=0.50): vol.All(vol.Range(min=0,max=10.0)), } ) SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Required("host_ipv4_address"): cv.string, vol.Required("host_ip_port", default=8200): vol.All(vol.Range(min=1,max=65535)), vol.Required("cpath"): cv.string, vol.Optional("version"): cv.string, vol.Optional("use_ssl"): cv.boolean, } ) SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("timeout", default=5): vol.All(vol.Range(min=1,max=10)), } ) # ----------------------------------------------------------------------------------- # Custom Service Schemas - MediaPlayerEntity enhancements. # ----------------------------------------------------------------------------------- SERVICE_VOLUME_SET_STEP_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, vol.Optional("level", default=None): vol.Any(None, vol.All(vol.Coerce(float), vol.Range(min=0.01, max=1.00))), vol.Optional("level_percent", default=None): vol.Any(None, vol.All(vol.Coerce(int), vol.Range(min=1, max=100))) } ) # ----------------------------------------------------------------------------------- # Custom Service Schemas - non-Spotify Web API related. # ----------------------------------------------------------------------------------- SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS_SCHEMA = vol.Schema( { vol.Optional("filter_domain"): cv.string, vol.Optional("filter_credentials_only", default=True): cv.boolean, vol.Optional("list_domain_entities", default=False): cv.boolean, } ) SERVICE_TEST_TOKEN_EXPIRE_SCHEMA = vol.Schema( { vol.Required("entity_id"): cv.entity_id, } ) def _get_file_contents_json(filePath: str, title: str) -> dict: """ Retrieves the contents of the specified JSON text file. Args: filePath (str): Fully-qualified file path whose contents will be read. title (str): Title to assign to the log entry. Returns: A dictionary object loaded from JSON file. """ result = None with open(filePath, "r") as f: result = json.load(f) return result def _trace_LogTextFile(filePath: str, title: str) -> None: """ Log the contents of the specified text file to the SmartInspect trace log. Args: filePath (str): Fully-qualified file path to log. title (str): Title to assign to the log entry. """ _logsi.LogTextFile(SILevel.Verbose, title, filePath) async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """ Set up the component. Args: hass (HomeAssistant): HomeAssistant instance. config (ConfigType): HomeAssistant validation configuration object. The __init__.py module "async_setup" method is executed once for the component configuration, no matter how many devices are configured for the component. It takes care of loading the services that the component provides, as well as the ConfigType dictionary. The ConfigType dictionary contains Home Assistant configuration entries that reference this component type: 'default_config', 'frontend' (themes, etc), 'automation', 'script', and 'scenes' sub-dictionaries. """ try: # trace. _logsi.EnterMethod(SILevel.Debug) if _logsi.IsOn(SILevel.Verbose): _logsi.LogVerbose("Component async_setup starting") # log the manifest file contents. # as of HA 2024.6, we have to use an executor job to do this as the trace uses a blocking file open / read call. myConfigDir:str = "%s/custom_components/%s" % (hass.config.config_dir, DOMAIN) myManifestPath:str = "%s/manifest.json" % (myConfigDir) await hass.async_add_executor_job(_trace_LogTextFile, myManifestPath, "Integration Manifest File (%s)" % myManifestPath) # log verion information for supporting packages. _logsi.LogValue(SILevel.Verbose, "urllib3 version", urllib3_version) _logsi.LogValue(SILevel.Verbose, "spotifywebapipython version", spotifywebapipython_VERSION, colorValue=SIColors.Coral) # log configuration data. for item in config: itemKey:str = str(item) itemObj = config[itemKey] if isinstance(itemObj,dict): _logsi.LogDictionary(SILevel.Verbose, "ConfigType '%s' data (dictionary)" % itemKey, itemObj, prettyPrint=True) elif isinstance(itemObj,list): _logsi.LogArray(SILevel.Verbose, "ConfigType '%s' data (list)" % itemKey, itemObj) else: _logsi.LogObject(SILevel.Verbose, "ConfigType '%s' data (object)" % (itemKey), itemObj) if DOMAIN in config: # remind user to remove SpotifyPlus settings from configuration.yaml if present. async_create_issue( hass, DOMAIN, "removed_yaml", breaks_in_ha_version="2022.8.0", is_fixable=False, severity=IssueSeverity.WARNING, translation_key="removed_yaml", ) # storage directory path. storage_dir = hass.config.path(".storage") async def service_handle_spotify_command(service: ServiceCall) -> None: """ Handle service requests that do not return service response data from Spotify endpoints. Args: service (ServiceCall): ServiceCall instance that contains service data (requested service name, field parameters, etc). """ try: _logsi.EnterMethod(SILevel.Debug) _logsi.LogVerbose(STAppMessages.MSG_SERVICE_CALL_START, service.service, 'service_handle_spotify_command') _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_PARM, service) _logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_DATA, service.data) # get player instance from service parameter; if not found, then we are done. entity = _GetEntityFromServiceData(hass, service, "entity_id") if entity is None: return # process service request. if service.service == SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS: # add player queue items. uris = service.data.get("uris") device_id = service.data.get("device_id") verify_device_id = service.data.get("verify_device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_add_player_queue_items, uris, device_id, verify_device_id, delay) elif service.service == SERVICE_SPOTIFY_FOLLOW_ARTISTS: # follow artist(s). ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_follow_artists, ids) elif service.service == SERVICE_SPOTIFY_FOLLOW_PLAYLIST: # follow playlist. playlist_id = service.data.get("playlist_id") public = service.data.get("public") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_follow_playlist, playlist_id, public) elif service.service == SERVICE_SPOTIFY_FOLLOW_USERS: # follow user(s). ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_follow_users, ids) elif service.service == SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE: # get cover image file. image_url = service.data.get("image_url") output_path = service.data.get("output_path") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_get_cover_image_file, image_url, output_path) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE: # pause media play. device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_pause, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT: # start playing one or more tracks of the specified context. context_uri = service.data.get("context_uri") offset_uri = service.data.get("offset_uri") offset_position = service.data.get("offset_position") position_ms = service.data.get("position_ms") device_id = service.data.get("device_id") delay = service.data.get("delay") shuffle = service.data.get("shuffle") play_show_latest_episode = service.data.get("play_show_latest_episode") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_play_context, context_uri, offset_uri, offset_position, position_ms, device_id, delay, shuffle, play_show_latest_episode) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES: # start playing all track favorites. device_id = service.data.get("device_id") shuffle = service.data.get("shuffle") delay = service.data.get("delay") resolve_device_id = service.data.get("resolve_device_id") limit_total = service.data.get("limit_total") filter_artist = service.data.get("filter_artist") filter_album = service.data.get("filter_album") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_play_track_favorites, device_id, shuffle, delay, resolve_device_id, limit_total, filter_artist, filter_album) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS: # start playing one or more tracks. uris = service.data.get("uris") position_ms = service.data.get("position_ms") device_id = service.data.get("device_id") delay = service.data.get("delay") shuffle = service.data.get("shuffle") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_play_tracks, uris, position_ms, device_id, delay, shuffle) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME: # resume media play. device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_resume, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK: # seeks to the given position in the currently playing track. position_ms = service.data.get("position_ms") device_id = service.data.get("device_id") delay = service.data.get("delay") relative_position_ms = service.data.get("relative_position_ms") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_seek, position_ms, device_id, delay, relative_position_ms) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT: # skip to next track. device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_skip_next, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS: # skip to next track. device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_media_skip_previous, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE: # set player repeat mode. state = service.data.get("state") device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_set_repeat_mode, state, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE: # set player shuffle mode. state = service.data.get("state") device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_set_shuffle_mode, state, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL: # set player shuffle mode. volume_level = service.data.get("volume_level") device_id = service.data.get("device_id") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_set_volume_level, volume_level, device_id, delay) elif service.service == SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK: # transfer playback to a new Spotify Connect device. device_id = service.data.get("device_id") play = service.data.get("play") delay = service.data.get("delay") refresh_device_list = service.data.get("refresh_device_list") force_activate_device = service.data.get("force_activate_device") device_id_from = service.data.get("device_id_from") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_player_transfer_playback, device_id, play, delay, refresh_device_list, force_activate_device, device_id_from) elif service.service == SERVICE_SPOTIFY_PLAYLIST_CHANGE: # update details of an existing playlist. playlist_id = service.data.get("playlist_id") name = service.data.get("name") description = service.data.get("description") public = service.data.get("public") collaborative = service.data.get("collaborative") image_path = service.data.get("image_path") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_playlist_change, playlist_id, name, description, public, collaborative, image_path) elif service.service == SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD: # add playlist cover image. playlist_id = service.data.get("playlist_id") image_path = service.data.get("image_path") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_playlist_cover_image_add, playlist_id, image_path) elif service.service == SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES: # remove album(s) from favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_album_favorites, ids) elif service.service == SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES: # remove audiobook(s) from favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_audiobook_favorites, ids) elif service.service == SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES: # remove episode(s) from favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_episode_favorites, ids) elif service.service == SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES: # remove show(s) from favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_show_favorites, ids) elif service.service == SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES: # remove track(s) from favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_track_favorites, ids) elif service.service == SERVICE_SPOTIFY_REMOVE_USER_FAVORITES: # remove items from user library favorites. uris = service.data.get("uris") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_remove_user_favorites, uris) elif service.service == SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES: # save album(s) to favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_album_favorites, ids) elif service.service == SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES: # save audiobook(s) to favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_audiobook_favorites, ids) elif service.service == SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES: # save episode(s) to favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_episode_favorites, ids) elif service.service == SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES: # save show(s) to favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_show_favorites, ids) elif service.service == SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES: # save track(s) to favorites. ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_track_favorites, ids) elif service.service == SERVICE_SPOTIFY_SAVE_USER_FAVORITES: # save item(s) to user library favorites. uris = service.data.get("uris") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_save_user_favorites, uris) elif service.service == SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL: # test token expiration. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_trigger_scan_interval) elif service.service == SERVICE_SPOTIFY_UNFOLLOW_ARTISTS: # unfollow artist(s). ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_unfollow_artists, ids) elif service.service == SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST: # unfollow playlist. playlist_id = service.data.get("playlist_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_unfollow_playlist, playlist_id) elif service.service == SERVICE_SPOTIFY_UNFOLLOW_USERS: # unfollow user(s). ids = service.data.get("ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_spotify_unfollow_users, ids) elif service.service == SERVICE_TEST_TOKEN_EXPIRE: # test token expiration. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) await hass.async_add_executor_job(entity.service_test_token_expire) elif service.service == SERVICE_VOLUME_SET_STEP: # test token expiration. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) level = service.data.get("level") level_percent = service.data.get("level_percent") await hass.async_add_executor_job(entity.service_volume_set_step, level, level_percent) else: raise IntegrationError("Unrecognized service identifier \"%s\" in method \"service_handle_spotify_command\"." % service.service) # return (no response). return except HomeAssistantError as ex: # log error, but not to system logger as HA will take care of it. _logsi.LogError(str(ex), logToSystemLogger=False) raise except Exception as ex: # log exception, but not to system logger as HA will take care of it. _logsi.LogException(STAppMessages.MSG_SERVICE_REQUEST_EXCEPTION % (service.service, "service_handle_spotify_command"), ex, logToSystemLogger=False) raise finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def service_handle_spotify_serviceresponse(service: ServiceCall) -> ServiceResponse: """ Handle service requests that return service response data from Spotify endpoints. Args: service (ServiceCall): ServiceCall instance that contains service data (requested service name, field parameters, etc). """ try: _logsi.EnterMethod(SILevel.Debug) _logsi.LogVerbose(STAppMessages.MSG_SERVICE_CALL_START, service.service, 'service_handle_spotify_serviceresponse') _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_PARM, service) _logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_DATA, service.data) # get player instance from service parameter; if not found, then we are done. entity = _GetEntityFromServiceData(hass, service, "entity_id") if entity is None: return response:dict = {} # process service request. 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_PLAYLIST_FOLLOWERS: # check playlist followers. playlist_id = service.data.get("playlist_id") user_ids = service.data.get("user_ids") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_check_playlist_followers, playlist_id, user_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_CHECK_USER_FAVORITES: # check user library favorites. uris = service.data.get("uris") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_check_user_favorites, uris) elif service.service == SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING: # check users 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_users_following, ids) elif service.service == SERVICE_SPOTIFY_GET_ALBUM: # get spotify album. album_id = service.data.get("album_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_album, album_id, market) elif service.service == SERVICE_SPOTIFY_GET_ALBUM_FAVORITES: # get spotify album favorites. limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_album_favorites, limit, offset, market, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES: # get spotify album favorites. limit = service.data.get("limit") offset = service.data.get("offset") country = service.data.get("country") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_album_new_releases, limit, offset, country, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ALBUM_TRACKS: # get spotify album favorites. album_id = service.data.get("album_id") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_album_tracks, album_id, limit, offset, market, limit_total) elif service.service == SERVICE_SPOTIFY_GET_ARTIST: # get spotify artist. artist_id = service.data.get("artist_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artist, artist_id) elif service.service == SERVICE_SPOTIFY_GET_ARTIST_ALBUMS: # get spotify artist albums. artist_id = service.data.get("artist_id") include_groups = service.data.get("include_groups") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artist_albums, artist_id, include_groups, limit, offset, market, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ARTIST_INFO: # get spotify artist info. artist_id = service.data.get("artist_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artist_info, artist_id) elif service.service == SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS: # get spotify artist related artists. artist_id = service.data.get("artist_id") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artist_related_artists, artist_id, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS: # get spotify artist top artists. artist_id = service.data.get("artist_id") market = service.data.get("market") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artist_top_tracks, artist_id, market, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED: # get spotify artists followed. after = service.data.get("after") limit = service.data.get("limit") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_artists_followed, after, limit, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_AUDIOBOOK: # get spotify audiobook. audiobook_id = service.data.get("audiobook_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_audiobook, audiobook_id, market) elif service.service == SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS: # get spotify audiobook chapters. audiobook_id = service.data.get("audiobook_id") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_audiobook_chapters, audiobook_id, limit, offset, market, limit_total) elif service.service == SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES: # get spotify audiobook favorites. limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_audiobook_favorites, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST: # get spotify browse categorys. country = service.data.get("country") locale = service.data.get("locale") refresh = service.data.get("refresh") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_browse_categorys_list, country, locale, refresh) elif service.service == SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS: # get spotify featured playlists. category_id = service.data.get("category_id") limit = service.data.get("limit") offset = service.data.get("offset") country = service.data.get("country") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_category_playlists, category_id, limit, offset, country, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_CHAPTER: # get spotify audiobook chapter details. chapter_id = service.data.get("chapter_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_chapter, chapter_id, market) elif service.service == SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE: # get spotify device playback state. device_id = service.data.get("device_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_device_playback_state, device_id) elif service.service == SERVICE_SPOTIFY_GET_EPISODE: # get spotify episode details. episode_id = service.data.get("episode_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_episode, episode_id, market) elif service.service == SERVICE_SPOTIFY_GET_EPISODE_FAVORITES: # get spotify episode (podcast) favorites. limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_episode_favorites, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS: # get spotify featured playlists. limit = service.data.get("limit") offset = service.data.get("offset") country = service.data.get("country") locale = service.data.get("locale") timestamp = service.data.get("timestamp") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_featured_playlists, limit, offset, country, locale, timestamp, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_ID_FROM_URI: # get id from uri. uri = service.data.get("uri") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_id_from_uri, uri) elif service.service == SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS: # get image palette colors. image_source = service.data.get("image_source") color_count = service.data.get("color_count") color_quality = service.data.get("color_quality") brightness_filter_low = service.data.get("brightness_filter_low") brightness_filter_high = service.data.get("brightness_filter_high") hue_distance_filter = service.data.get("hue_distance_filter") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_image_palette_colors, image_source, color_count, color_quality, brightness_filter_low, brightness_filter_high, hue_distance_filter) elif service.service == SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS: # get image vibrant colors. image_source = service.data.get("image_source") color_count = service.data.get("color_count") color_quality = service.data.get("color_quality") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_image_vibrant_colors, image_source, color_count, color_quality) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_DEVICES: # get spotify player device list. refresh = service.data.get("refresh") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_devices, refresh, sort_result) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO: # get spotify player last played information. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_last_played_info) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING: # get spotify player now playing. market = service.data.get("market") additional_types = service.data.get("additional_types") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_now_playing, market, additional_types) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE: # get spotify player playback state. market = service.data.get("market") additional_types = service.data.get("additional_types") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_playback_state, market, additional_types) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO: # get spotify queue info. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_queue_info) elif service.service == SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS: # get spotify playlist favorites. limit = service.data.get("limit") after = service.data.get("after") before = service.data.get("before") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_player_recent_tracks, limit, after, before, limit_total) elif service.service == SERVICE_SPOTIFY_GET_PLAYLIST: # get spotify playlist. playlist_id = service.data.get("playlist_id") market = service.data.get("market") fields = service.data.get("fields") additional_types = service.data.get("additional_types") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_playlist, playlist_id, market, fields, additional_types) elif service.service == SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE: # get spotify playlist cover image. playlist_id = service.data.get("playlist_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_playlist_cover_image, playlist_id) elif service.service == SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES: # get spotify playlist favorites. limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_playlist_favorites, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS: # get spotify playlist items. playlist_id = service.data.get("playlist_id") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") fields = service.data.get("fields") additional_types = service.data.get("additional_types") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_playlist_items, playlist_id, limit, offset, market, fields, additional_types, limit_total) elif service.service == SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER: # get spotify playlists for specified user-id. user_id = service.data.get("user_id") limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_playlists_for_user, user_id, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_SHOW: # get spotify show (podcast) episodes. show_id = service.data.get("show_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_show, show_id, market) elif service.service == SERVICE_SPOTIFY_GET_SHOW_EPISODES: # get spotify show (podcast) episodes. show_id = service.data.get("show_id") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_show_episodes, show_id, limit, offset, market, limit_total) elif service.service == SERVICE_SPOTIFY_GET_SHOW_FAVORITES: # get spotify show (podcast) favorites. limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") exclude_audiobooks = service.data.get("exclude_audiobooks") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_show_favorites, limit, offset, limit_total, sort_result, exclude_audiobooks) elif service.service == SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE: # get spotify connect device list. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) device_value = service.data.get("device_value") verify_user_context = service.data.get("verify_user_context") verify_timeout = service.data.get("verify_timeout") refresh_device_list = service.data.get("refresh_device_list") activate_device = service.data.get("activate_device") delay = service.data.get("delay") response = await hass.async_add_executor_job(entity.service_spotify_get_spotify_connect_device, device_value, verify_user_context, verify_timeout, refresh_device_list, activate_device, delay) elif service.service == SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES: # get spotify connect device list. _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) refresh = service.data.get("refresh") sort_result = service.data.get("sort_result") response = await hass.async_add_executor_job(entity.service_spotify_get_spotify_connect_devices, refresh, sort_result) elif service.service == SERVICE_SPOTIFY_GET_TRACK: # get spotify track details. track_id = service.data.get("track_id") market = service.data.get("market") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_track, track_id, market) elif service.service == SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES: # get spotify album favorites. limit = service.data.get("limit") track_id = service.data.get("track_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_track_audio_features, track_id) elif service.service == SERVICE_SPOTIFY_GET_TRACK_FAVORITES: # get spotify track favorites. limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") filter_artist = service.data.get("filter_artist") filter_album = service.data.get("filter_album") _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, filter_artist, filter_album) elif service.service == SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS: # get spotify track recommendations. limit = service.data.get("limit") market = service.data.get("market") seed_artists = service.data.get("seed_artists") seed_genres = service.data.get("seed_genres") seed_tracks = service.data.get("seed_tracks") min_acousticness = service.data.get("min_acousticness") max_acousticness = service.data.get("max_acousticness") target_acousticness = service.data.get("target_acousticness") min_danceability = service.data.get("min_danceability") max_danceability = service.data.get("max_danceability") target_danceability = service.data.get("target_danceability") min_duration_ms = service.data.get("min_duration_ms") max_duration_ms = service.data.get("max_duration_ms") target_duration_ms = service.data.get("target_duration_ms") min_energy = service.data.get("min_energy") max_energy = service.data.get("max_energy") target_energy = service.data.get("target_energy") min_instrumentalness = service.data.get("min_instrumentalness") max_instrumentalness = service.data.get("max_instrumentalness") target_instrumentalness = service.data.get("target_instrumentalness") min_key = service.data.get("min_key") max_key = service.data.get("max_key") target_key = service.data.get("target_key") min_liveness = service.data.get("min_liveness") max_liveness = service.data.get("max_liveness") target_liveness = service.data.get("target_liveness") min_loudness = service.data.get("min_loudness") max_loudness = service.data.get("max_loudness") target_loudness = service.data.get("target_loudness") min_mode = service.data.get("min_mode") max_mode = service.data.get("max_mode") target_mode = service.data.get("target_mode") min_popularity = service.data.get("min_popularity") max_popularity = service.data.get("max_popularity") target_popularity = service.data.get("target_popularity") min_speechiness = service.data.get("min_speechiness") max_speechiness = service.data.get("max_speechiness") target_speechiness = service.data.get("target_speechiness") min_tempo = service.data.get("min_tempo") max_tempo = service.data.get("max_tempo") target_tempo = service.data.get("target_tempo") min_time_signature = service.data.get("min_time_signature") max_time_signature = service.data.get("max_time_signature") target_time_signature = service.data.get("target_time_signature") min_valence = service.data.get("min_valence") max_valence = service.data.get("max_valence") target_valence = service.data.get("target_valence") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job( entity.service_spotify_get_track_recommendations, limit, market, seed_artists, seed_genres, seed_tracks, min_acousticness, max_acousticness, target_acousticness, min_danceability, max_danceability, target_danceability, min_duration_ms, max_duration_ms, target_duration_ms, min_energy, max_energy, target_energy, min_instrumentalness, max_instrumentalness, target_instrumentalness, min_key, max_key, target_key, min_liveness, max_liveness, target_liveness, min_loudness, max_loudness, target_loudness, min_mode, max_mode, target_mode, min_popularity, max_popularity, target_popularity, min_speechiness, max_speechiness, target_speechiness, min_tempo, max_tempo, target_tempo, min_time_signature, max_time_signature, target_time_signature, min_valence, max_valence, target_valence) 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. time_range = service.data.get("time_range") limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_users_top_artists, time_range, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS: # get spotify users top artists. time_range = service.data.get("time_range") limit = service.data.get("limit") offset = service.data.get("offset") limit_total = service.data.get("limit_total") sort_result = service.data.get("sort_result") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_get_users_top_tracks, time_range, limit, offset, limit_total, sort_result) elif service.service == SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD: # add items to playlist. playlist_id = service.data.get("playlist_id") uris = service.data.get("uris") position = service.data.get("position") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_items_add, playlist_id, uris, position) elif service.service == SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR: # clear all items from playlist. playlist_id = service.data.get("playlist_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_items_clear, playlist_id) elif service.service == SERVICE_SPOTIFY_PLAYLIST_CREATE: # create a new playlist. user_id = service.data.get("user_id") name = service.data.get("name") description = service.data.get("description") public = service.data.get("public") collaborative = service.data.get("collaborative") image_path = service.data.get("image_path") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_create, user_id, name, description, public, collaborative, image_path) elif service.service == SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE: # add items to playlist. playlist_id = service.data.get("playlist_id") uris = service.data.get("uris") snapshot_id = service.data.get("snapshot_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_items_remove, playlist_id, uris, snapshot_id) elif service.service == SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER: # reorder playlist items. playlist_id = service.data.get("playlist_id") range_start = service.data.get("range_start") insert_before = service.data.get("insert_before") range_length = service.data.get("range_length") snapshot_id = service.data.get("snapshot_id") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_items_reorder, playlist_id, range_start, insert_before, range_length, snapshot_id) elif service.service == SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE: # replace playlist items. playlist_id = service.data.get("playlist_id") uris = service.data.get("uris") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_playlist_items_replace, playlist_id, uris) elif service.service == SERVICE_SPOTIFY_SEARCH_ALL: # search Spotify for specified criteria. criteria = service.data.get("criteria") criteria_type = service.data.get("criteria_type") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_all, criteria, criteria_type, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_ALBUMS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_albums, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_ARTISTS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_artists, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_audiobooks, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_EPISODES: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_episodes, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_PLAYLISTS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_playlists, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_SHOWS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_shows, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_SEARCH_TRACKS: # search Spotify for specified criteria. criteria = service.data.get("criteria") limit = service.data.get("limit") offset = service.data.get("offset") market = service.data.get("market") include_external = service.data.get("include_external") limit_total = service.data.get("limit_total") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_search_tracks, criteria, limit, offset, market, include_external, limit_total) elif service.service == SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT: # spotify connect zeroconf device connect. host_ipv4_address = service.data.get("host_ipv4_address") host_ip_port = service.data.get("host_ip_port") cpath = service.data.get("cpath") version = service.data.get("version") use_ssl = service.data.get("use_ssl") username = service.data.get("username") password = service.data.get("password") loginid = service.data.get("loginid") pre_disconnect = service.data.get("pre_disconnect") verify_device_list_entry = service.data.get("verify_device_list_entry") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_zeroconf_device_connect, username, password, loginid, host_ipv4_address, host_ip_port, cpath, version, use_ssl, pre_disconnect, verify_device_list_entry, delay) elif service.service == SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT: # spotify connect zeroconf device disconnect. host_ipv4_address = service.data.get("host_ipv4_address") host_ip_port = service.data.get("host_ip_port") cpath = service.data.get("cpath") version = service.data.get("version") use_ssl = service.data.get("use_ssl") delay = service.data.get("delay") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_zeroconf_device_disconnect, host_ipv4_address, host_ip_port, cpath, version, use_ssl, delay) elif service.service == SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO: # spotify connect zeroconf device get information. host_ipv4_address = service.data.get("host_ipv4_address") host_ip_port = service.data.get("host_ip_port") cpath = service.data.get("cpath") version = service.data.get("version") use_ssl = service.data.get("use_ssl") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_zeroconf_device_getinfo, host_ipv4_address, host_ip_port, cpath, version, use_ssl) elif service.service == SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES: # zeroconf discover devices service. timeout = service.data.get("timeout") _logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name)) response = await hass.async_add_executor_job(entity.service_spotify_zeroconf_discover_devices, timeout) else: raise IntegrationError("Unrecognized service identifier \"%s\" in method \"service_handle_spotify_serviceresponse\"." % service.service) # return the response. _logsi.LogDictionary(SILevel.Verbose, "Service Response data: '%s'" % (service.service), response, prettyPrint=True) return response except HomeAssistantError as ex: # log error, but not to system logger as HA will take care of it. _logsi.LogError(str(ex), logToSystemLogger=False) raise except Exception as ex: # log exception, but not to system logger as HA will take care of it. _logsi.LogException(STAppMessages.MSG_SERVICE_REQUEST_EXCEPTION % (service.service, "service_handle_spotify_serviceresponse"), ex, logToSystemLogger=False) raise finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def service_list_application_credential_mappings(service: ServiceCall): """ List Application Credential mappings. Args: service (ServiceCall): ServiceCall instance that contains service data (requested service name, field parameters, etc). """ apiMethodName:str = "service_list_application_credential_mappings" try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogVerbose(STAppMessages.MSG_SERVICE_CALL_START, service.service, apiMethodName) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_PARM, service) _logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_SERVICE_CALL_DATA, service.data) # get optional filter criteria. filter_domain = service.data.get("filter_domain") filter_credentials_only = service.data.get("filter_credentials_only") list_domain_entities = service.data.get("list_domain_entities") try: _logsi.LogVerbose("Opening configuration files") # read file contents via an "async_add_executor_job" to avoid the following # error: "Detected blocking call to open with args inside the event loop app_creds = await hass.async_add_executor_job(_get_file_contents_json, os.path.join(storage_dir, "application_credentials"), "Application Credentials") config_entries = await hass.async_add_executor_job(_get_file_contents_json, os.path.join(storage_dir, "core.config_entries"), "Core Config Entries") entity_registry = await hass.async_add_executor_job(_get_file_contents_json, os.path.join(storage_dir, "core.entity_registry"), "Core Entity Registry") except Exception as e: # log exception, but not to system logger as HA will take care of it. _logsi.LogException("AppCred Mapper - Error reading storage files: %s" % (str(ex)), ex, logToSystemLogger=False) return # Map domain to application credentials. # A domain may have multiple application credentials. _logsi.LogVerbose("Mapping Domain to Credentials") domain_to_creds = {} for cred in app_creds.get("data", {}).get("items", []): domain = cred.get("domain") domain_to_creds.setdefault(domain, []).append({ "id": cred.get("id"), "client_id": cred.get("client_id"), "name": cred.get("name"), }) _logsi.LogDictionary(SILevel.Verbose, "Domain to Credentials dictionary", domain_to_creds) # Map config entry id to domain. _logsi.LogVerbose("Mapping Config Entry ID to Domain") entry_to_domain = {} for entry in config_entries.get("data", {}).get("entries", []): config_entry_id = entry.get("entry_id") domain = entry.get("domain") if not domain: continue if filter_domain and domain != filter_domain: continue entry_to_domain.setdefault(config_entry_id, domain) _logsi.LogDictionary(SILevel.Verbose, "Config Entry ID to Domain dictionary", entry_to_domain) # Map config entry id to application credential. # A config entry only has a single (or none) application credential. _logsi.LogVerbose("Mapping Config Entry ID to Application Credential") entry_to_creds = {} for entry in config_entries.get("data", {}).get("entries", []): config_entry_id = entry.get("entry_id") domain = entry.get("domain") entry_data = entry.get("data", {}) auth_implementation = entry_data.get("auth_implementation") if not domain: continue if filter_domain and domain != filter_domain: continue if filter_credentials_only and (auth_implementation is None): continue #entry_to_creds.setdefault(config_entry_id, []).append({ entry_to_creds.setdefault(config_entry_id, { "domain": domain, "name": entry_data.get("name"), "title": entry.get("title"), "auth_implementation": auth_implementation, "id": entry_data.get("id"), # could be null "description": entry_data.get("description"), # could be null }) _logsi.LogDictionary(SILevel.Verbose, "Config Entry ID to Application Credential dictionary", entry_to_creds) _logsi.LogDictionary(SILevel.Verbose, "Config Entry ID to Application Credential dictionary (prettyPrint)", entry_to_creds, prettyPrint=True) # Map entity id to application credential. # An entity only has a single (or none) application credential. _logsi.LogVerbose("Mapping Entity ID to Application Credential") entity_to_creds = {} for ent in entity_registry.get("data", {}).get("entities", []): config_entry_id = ent["config_entry_id"] domain = entry_to_domain.get(config_entry_id) credential = entry_to_creds.get(config_entry_id, {}) entity_id = ent.get("entity_id") name = ent.get("name") or ent.get("original_name") id_value = ent.get("id") if not domain: continue if filter_domain and domain != filter_domain: continue if filter_credentials_only and (credential == {}): continue #entity_to_creds.setdefault(id_value, []).append({ entity_to_creds.setdefault(id_value, { "domain": domain, "entity_id": entity_id, "name": name, "config_entry_id": config_entry_id, "credential": credential, }) _logsi.LogDictionary(SILevel.Verbose, "Entity ID to Application Credential dictionary", entity_to_creds) _logsi.LogDictionary(SILevel.Verbose, "Entity ID to Application Credential dictionary (prettyPrint)", entity_to_creds, prettyPrint=True) # Map entities to domains/creds. # An entity only has a single (or none) application credential. _logsi.LogVerbose("Mapping Entities to Domains / Credentials") results = {} for ent in entity_registry.get("data", {}).get("entities", []): config_entry_id = ent["config_entry_id"] domain = entry_to_domain.get(config_entry_id) id_value = ent.get("id") entity_id = ent["entity_id"] name = ent["name"] or ent["original_name"] credential = entity_to_creds.get(id_value, {}) if not domain: continue if filter_domain and domain != filter_domain: continue if filter_credentials_only and (credential == {}): continue results.setdefault(domain, []).append({ #results.setdefault(domain, { "id": id_value, "entity_id": entity_id, "unique_id": ent.get("unique_id"), "name": name, "credential": credential }) _logsi.LogDictionary(SILevel.Verbose, "Entities to Domains / Credentials dictionary", results) _logsi.LogDictionary(SILevel.Verbose, "Entities to Domains / Credentials dictionary (prettyPrint)", results, prettyPrint=True) # Format text. _logsi.LogVerbose("Formatting results") lines = [] for domain, entities in results.items(): lines.append(f"**Domain: {domain}**") # list application credentials. credentials = domain_to_creds.get(domain, []) lines.append(" Application Credentials:") if credentials: for c in credentials: lines.append(f" - ID: `{c['id']}`, Client ID: `{c['client_id']}`, Name: `{c['name']}`") for e in entities: entity_cred_id = e.get("credential", {}).get("credential", {}).get("auth_implementation") if c['id'] == entity_cred_id: lines.append(f" - Entity ID: `{e['entity_id']}`, Name: `{e['name']}`, Unique ID: `{e['unique_id']}`") else: lines.append(" - No credentials found") # list defined entities. if (list_domain_entities): lines.append(" Entities:") for e in entities: lines.append(f" - ID: `{e['entity_id']}`, Name: `{e['name']}`, Unique ID: `{e['unique_id']}`") lines.append("") text = "\n".join(lines) if lines else ( f"No entities found for domain '{filter_domain}'" if filter_domain else "No entities found." ) # Log and return results. _logsi.LogText(SILevel.Message, "List Application Credential Mappings by Domain result", text) _LOGGER.info("Application Credential Mapping:\n%s", text) # return the result as a dictionary. return { "result": text } except HomeAssistantError as ex: # log error, but not to system logger as HA will take care of it. _logsi.LogError(str(ex), logToSystemLogger=False) raise except Exception as ex: # log exception, but not to system logger as HA will take care of it. _logsi.LogException(STAppMessages.MSG_SERVICE_REQUEST_EXCEPTION % (service.service, apiMethodName), ex, logToSystemLogger=False) raise finally: # trace. _logsi.LeaveMethod(SILevel.Debug) @staticmethod def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:str) -> MediaPlayerEntity: """ Resolves a `MediaPlayerEntity` instance from a ServiceCall field id. Args: hass (HomeAssistant): HomeAssistant instance. service (ServiceCall): ServiceCall instance that contains service data (requested service name, field parameters, etc). field_id (str): Service parameter field id whose value contains a SpotifyMediaPlayer entity id. The ServiceCall data area will be queried with the field id to retrieve the entity id value. Returns: A `MediaPlayerEntity` instance if one could be resolved; otherwise, None. The service.data collection is queried for the field_id argument name. If not supplied, then an error message is logged and the return value is None. The Haas data is then queried for the entity_id to retrieve the `MediaPlayerEntity` instance. """ # get service parameter: entity_id. entity_id = service.data.get(field_id) if entity_id is None: _logsi.LogError(STAppMessages.MSG_SERVICE_ARGUMENT_NULL, field_id, service.service) return None # search all MediaPlayerEntity instances for the specified entity_id. # if found, then return the MediaPlayerEntity instance. player:MediaPlayerEntity = None data:InstanceDataSpotifyPlus = None for data in hass.data[DOMAIN].values(): if data.media_player.entity_id == entity_id: player = data.media_player break # did we resolve it? if not, then log a message. if player is None: raise ServiceValidationError("Entity id value of '%s' could not be resolved to a MediaPlayerEntity instance for the '%s' method call" % (str(entity_id), service.service)) # return the MediaPlayerEntity instance. _logsi.LogVerbose("Entity id value of '%s' was resolved to MediaPlayerEntity instance for the '%s' method call" % (str(entity_id), service.service)) return player # register all services this component provides, and their corresponding schemas. _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS, SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_ADD_PLAYER_QUEUE_ITEMS_SCHEMA, supports_response=SupportsResponse.NONE, ) _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_PLAYLIST_FOLLOWERS, SERVICE_SPOTIFY_CHECK_PLAYLIST_FOLLOWERS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_CHECK_PLAYLIST_FOLLOWERS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_CHECK_PLAYLIST_FOLLOWERS_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_CHECK_USER_FAVORITES, SERVICE_SPOTIFY_CHECK_USER_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_CHECK_USER_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_CHECK_USER_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING, SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_CHECK_USERS_FOLLOWING_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, SERVICE_SPOTIFY_FOLLOW_ARTISTS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_FOLLOW_ARTISTS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_FOLLOW_PLAYLIST, SERVICE_SPOTIFY_FOLLOW_PLAYLIST_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_FOLLOW_PLAYLIST, service_handle_spotify_command, schema=SERVICE_SPOTIFY_FOLLOW_PLAYLIST_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_FOLLOW_USERS, SERVICE_SPOTIFY_FOLLOW_USERS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_FOLLOW_USERS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_FOLLOW_USERS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ALBUM, SERVICE_SPOTIFY_GET_ALBUM_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ALBUM, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ALBUM_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ALBUM_FAVORITES, SERVICE_SPOTIFY_GET_ALBUM_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ALBUM_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ALBUM_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES, SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ALBUM_NEW_RELEASES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ALBUM_TRACKS, SERVICE_SPOTIFY_GET_ALBUM_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ALBUM_TRACKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ALBUM_TRACKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTIST, SERVICE_SPOTIFY_GET_ARTIST_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTIST, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTIST_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTIST_ALBUMS, SERVICE_SPOTIFY_GET_ARTIST_ALBUMS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTIST_ALBUMS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTIST_ALBUMS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTIST_INFO, SERVICE_SPOTIFY_GET_ARTIST_INFO_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTIST_INFO, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTIST_INFO_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS, SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTIST_RELATED_ARTISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS, SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTIST_TOP_TRACKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED, SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ARTISTS_FOLLOWED_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_AUDIOBOOK, SERVICE_SPOTIFY_GET_AUDIOBOOK_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_AUDIOBOOK, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_AUDIOBOOK_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS, SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_AUDIOBOOK_CHAPTERS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_AUDIOBOOK_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST, SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_BROWSE_CATEGORYS_LIST_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS, SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_CATEGORY_PLAYLISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_CHAPTER, SERVICE_SPOTIFY_GET_CHAPTER_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_CHAPTER, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_CHAPTER_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE, SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE, service_handle_spotify_command, schema=SERVICE_SPOTIFY_GET_COVER_IMAGE_FILE_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE, SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_DEVICE_PLAYBACK_STATE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_EPISODE, SERVICE_SPOTIFY_GET_EPISODE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_EPISODE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_EPISODE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_EPISODE_FAVORITES, SERVICE_SPOTIFY_GET_EPISODE_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_EPISODE_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_EPISODE_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS, SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_FEATURED_PLAYLISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_ID_FROM_URI, SERVICE_SPOTIFY_GET_ID_FROM_URI_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_ID_FROM_URI, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_ID_FROM_URI_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS, SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_IMAGE_PALETTE_COLORS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS, SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_IMAGE_VIBRANT_COLORS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_DEVICES, SERVICE_SPOTIFY_GET_PLAYER_DEVICES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_DEVICES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_DEVICES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE, SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_PLAYBACK_STATE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO, SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_LAST_PLAYED_INFO_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING, SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_NOW_PLAYING_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO, SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_QUEUE_INFO_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS, SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYER_RECENT_TRACKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYLIST, SERVICE_SPOTIFY_GET_PLAYLIST_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYLIST, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYLIST_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE, SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYLIST_COVER_IMAGE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES, SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYLIST_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS, SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYLIST_ITEMS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER, SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_PLAYLISTS_FOR_USER_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_SHOW, SERVICE_SPOTIFY_GET_SHOW_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_SHOW, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_SHOW_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_SHOW_EPISODES, SERVICE_SPOTIFY_GET_SHOW_EPISODES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_SHOW_EPISODES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_SHOW_EPISODES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_SHOW_FAVORITES, SERVICE_SPOTIFY_GET_SHOW_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_SHOW_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_SHOW_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_SPOTIFY_CONNECT_DEVICES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_TRACK, SERVICE_SPOTIFY_GET_TRACK_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_TRACK, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_TRACK_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES, SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_TRACK_AUDIO_FEATURES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_TRACK_FAVORITES, SERVICE_SPOTIFY_GET_TRACK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_TRACK_FAVORITES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_TRACK_FAVORITES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS, SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_TRACK_RECOMMENDATIONS_SCHEMA, 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, SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS, SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE, SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_PAUSE_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_CONTEXT_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME, SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_RESUME_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK, SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_SEEK_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_NEXT_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_MEDIA_SKIP_PREVIOUS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE, SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE, SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL, SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK, SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD, SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_CHANGE, SERVICE_SPOTIFY_PLAYLIST_CHANGE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_CHANGE, service_handle_spotify_command, schema=SERVICE_SPOTIFY_PLAYLIST_CHANGE_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_CREATE, SERVICE_SPOTIFY_PLAYLIST_CREATE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_CREATE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_CREATE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD, SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_ITEMS_ADD_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR, SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_ITEMS_CLEAR_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_ITEMS_REMOVE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_ITEMS_REORDER_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_PLAYLIST_ITEMS_REPLACE_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES, SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES, SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES, SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES, SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_REMOVE_USER_FAVORITES, SERVICE_SPOTIFY_REMOVE_USER_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_REMOVE_USER_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_REMOVE_USER_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES, SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES, SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES, SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES, SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES, SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SAVE_USER_FAVORITES, SERVICE_SPOTIFY_SAVE_USER_FAVORITES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SAVE_USER_FAVORITES, service_handle_spotify_command, schema=SERVICE_SPOTIFY_SAVE_USER_FAVORITES_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_ALL, SERVICE_SPOTIFY_SEARCH_ALL_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_ALL, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_ALL_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_ALBUMS, SERVICE_SPOTIFY_SEARCH_ALBUMS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_ALBUMS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_ALBUMS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_ARTISTS, SERVICE_SPOTIFY_SEARCH_ARTISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_ARTISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_ARTISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS, SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_EPISODES, SERVICE_SPOTIFY_SEARCH_EPISODES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_EPISODES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_EPISODES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_PLAYLISTS, SERVICE_SPOTIFY_SEARCH_PLAYLISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_PLAYLISTS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_PLAYLISTS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_SHOWS, SERVICE_SPOTIFY_SEARCH_SHOWS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_SHOWS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_SHOWS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_SEARCH_TRACKS, SERVICE_SPOTIFY_SEARCH_TRACKS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_SEARCH_TRACKS, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_SEARCH_TRACKS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL, SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL, service_handle_spotify_command, schema=SERVICE_SPOTIFY_TRIGGER_SCAN_INTERVAL_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_UNFOLLOW_ARTISTS, SERVICE_SPOTIFY_UNFOLLOW_ARTISTS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_UNFOLLOW_ARTISTS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_UNFOLLOW_ARTISTS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST, SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST, service_handle_spotify_command, schema=SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_UNFOLLOW_USERS, SERVICE_SPOTIFY_UNFOLLOW_USERS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_UNFOLLOW_USERS, service_handle_spotify_command, schema=SERVICE_SPOTIFY_UNFOLLOW_USERS_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT, SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_ZEROCONF_DEVICE_CONNECT_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT, SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_ZEROCONF_DEVICE_DISCONNECT_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO, SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_ZEROCONF_DEVICE_GETINFO_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES, SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES, service_handle_spotify_serviceresponse, schema=SERVICE_SPOTIFY_ZEROCONF_DISCOVER_DEVICES_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_VOLUME_SET_STEP, SERVICE_VOLUME_SET_STEP_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_VOLUME_SET_STEP, service_handle_spotify_command, schema=SERVICE_VOLUME_SET_STEP_SCHEMA, supports_response=SupportsResponse.NONE, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS, SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS, service_list_application_credential_mappings, schema=SERVICE_LIST_APPLICATION_CREDENTIAL_MAPPPINGS_SCHEMA, supports_response=SupportsResponse.ONLY, ) _logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_TEST_TOKEN_EXPIRE, SERVICE_TEST_TOKEN_EXPIRE_SCHEMA) hass.services.async_register( DOMAIN, SERVICE_TEST_TOKEN_EXPIRE, service_handle_spotify_command, schema=SERVICE_TEST_TOKEN_EXPIRE_SCHEMA, supports_response=SupportsResponse.NONE, ) # indicate success. _logsi.LogVerbose("Component async_setup complete") return True finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def async_setup_entry(hass:HomeAssistant, entry:ConfigEntry) -> bool: """ Set up device instance from a config entry. Args: hass (HomeAssistant): HomeAssistant instance. entry (ConfigEntry): HomeAssistant configuration entry dictionary. This contains configuration settings for the specific component device entry. The __init__.py module "async_setup_entry" method is executed for each device that is configured for the component. It takes care of loading the device controller instance (e.g. SpotifyClient in our case) for each device that will be controlled. """ try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry is starting - entry (ConfigEntry) object" % entry.title, entry) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_setup_entry entry.data dictionary" % entry.title, entry.data) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_setup_entry entry.options dictionary" % entry.title, entry.options) # load config entry user-specified parameters. # ** no custom options for this integration. spotifyClient:SpotifyClient = None # get OAuth2 implementation and create an OAuth2 session. implementation = await async_get_config_entry_implementation(hass, entry) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry OAuth2 implementation object" % entry.title, implementation) session = OAuth2Session(hass, entry, implementation) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry OAuth2 session object" % entry.title, session) # ensure we have a valid session token, and that the session is fully created. _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_setup_entry OAuth2 session.token (dictionary)" % entry.title, session.token, prettyPrint=True) _logsi.LogVerbose("'%s': Component async_setup_entry is calling async_ensure_token_valid to ensure OAuth2 session is fully-established" % entry.title) await session.async_ensure_token_valid() # ----------------------------------------------------------------------------------- # Define OAuth2 Session Token Updater. # ----------------------------------------------------------------------------------- def _TokenUpdater() -> dict: """ Callback function that will inform HA OAuth2 that a token needs to be refreshed. This method is called from SpotifyClient whenever a token needs to be refreshed. Returns: A dictionaty that contains the refreshed token. """ token:dict = None # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is preparing to set the lock for method _TokenUpdater" % entry.title, colorValue=SIColors.Gold) if TOKENUPDATER_LOCK.locked(): _logsi.LogVerbose("'%s': _TokenUpdater is waiting on a previous token update to complete" % entry.title, colorValue=SIColors.Orange) # only allow one thread to update authorization token at a time; otherwise a deadlock # occurs in the `run_coroutine_threadsafe(session.implementation.async_refresh_token)` line! with TOKENUPDATER_LOCK: # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is set for method _TokenUpdater" % entry.title, colorValue=SIColors.Gold) try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogVerbose("'%s': Component OAuth2 session token is either expired, not valid, or just refreshed by another HA worker thread; starting token refresh processing" % entry.title, colorValue=SIColors.Gold) # note that we do NOT use the `async_ensure_token_valid` method here, as it may # determine that the token does not yet need to be refreshed (via self.valid_token). # at this point, the SpotifyClient instance KNOWS that the token needs to be refreshed. # we will call HA to refresh the token, and store the new token in the integration config entry. # # note that the `config_entry.data['token']` and `session.token` values should match, BUT # the refreshed `session.token` value will not change until AFTER `async_update_entry` is processed! # # the following steps will be performed to accomplish the above: # - get a reference to the HA OAuth2 session instance. # - call `async_refresh_token` to refresh the expired token. # - set a token `status` attribute to denote the configuration update is for a token refresh event. this allows the # `options_update_listener` to bypass the configuration reload processing since we only need to reload the configuration # if the user initiated an options change via the UI (e.g. no need to reload the configuration for token updates). # - call the `add_job` method to add a job that will call `async_update_entry` to persist the refreshed token to config storage. # - return the refreshed token to the caller. # trace. _logsi.LogObject(SILevel.Debug, "'%s': Component OAuth2 implementation object" % entry.title, implementation, colorValue=SIColors.Gold) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component OAuth2 session.token (pre-update, dictionary)" % entry.title, session.token, prettyPrint=True, colorValue=SIColors.Gold) # get formatted token, to make expiration checks easier. formattedToken0:SpotifyAuthToken = SpotifyAuthToken("TokenRefreshAuthType", "TokenRefreshProfileId", root=session.token) _logsi.LogObject(SILevel.Verbose, "'%s': Component OAuth2 session token (pre-update, session.token)" % entry.title, formattedToken0, excludeNonPublic=True, colorValue=SIColors.Gold) # a quick check to see if session token is expired. the session token update may have already occured # by another HA worker thread while we were waiting for the lock to free; if that is the case, then we # don't need to refresh the token since it was just refreshed by the other thread! if (not formattedToken0.IsExpired): _logsi.LogObject(SILevel.Verbose, "'%s': Component OAuth2 session.token was updated by another HA worker thread; refresh not necessary" % entry.title, formattedToken0, excludeNonPublic=True, colorValue=SIColors.Gold) return session.token # we will refresh the token from the `session.config_entry.data['token']` value (instead of # the `session.token` value) in case the token was refreshed in the `update` method. # note - the `session.token` value will not change until AFTER `async_update_entry` is processed! # refresh the session token. _logsi.LogVerbose("'%s': Component is calling async_refresh_token to refresh the session token" % entry.title, colorValue=SIColors.Gold) token = run_coroutine_threadsafe( session.implementation.async_refresh_token(session.config_entry.data['token']), hass.loop ).result() token[TOKEN_STATUS] = TOKEN_STATUS_REFRESH_EVENT # update token value in configuration entry data. # updating a config entry must be done in the event loop thread, as there is no sync API to update config entries! # the "hass.add_job" method is used to schedule a function in the event loop that calls hass.config_entries.async_update_entry. _logsi.LogDictionary(SILevel.Verbose, "'%s': Component is submitting add_job to call async_update_entry to update configuration entry data with refreshed token" % entry.title, token, prettyPrint=True, colorValue=SIColors.Gold) session.hass.add_job( functools.partial( session.hass.config_entries.async_update_entry, session.config_entry, data={**session.config_entry.data, "token": token} ) ) # trace. _logsi.LogVerbose("'%s': Component OAuth2 session token refresh complete" % entry.title, colorValue=SIColors.Gold) formattedToken2:SpotifyAuthToken = SpotifyAuthToken("TokenRefreshAuthType", "TokenRefreshProfileId", root=token) _logsi.LogObject(SILevel.Verbose, "'%s': Component OAuth2 session token (post-update, token)" % entry.title, formattedToken2, excludeNonPublic=True, colorValue=SIColors.Gold) # return refreshed token to caller. return token except Exception as ex: # trace. _logsi.LogException("'%s': Component OAuth2 session token refresh exception: %s" % (entry.title, str(ex)), ex, colorValue=SIColors.Gold) raise finally: # trace. _logsi.LeaveMethod(SILevel.Debug) # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is RESET for method _TokenUpdater" % entry.title, colorValue=SIColors.Gold) # ----------------------------------------------------------------------------------- # Continue with async_setup_entry # ----------------------------------------------------------------------------------- # get shared zeroconf instance. _logsi.LogVerbose("'%s': MediaPlayer async_setup_entry is storing the Zeroconf reference to the instanceData object" % entry.title) zeroconf_instance = await zeroconf.async_get_instance(hass) # create new spotify web api python client instance - "SpotifyClient()". _logsi.LogVerbose("'%s': Component async_setup_entry is creating SpotifyClient instance" % entry.title) tokenStorageDir:str = "%s/.storage" % (hass.config.config_dir) tokenStorageFile:str = "%s_tokens.json" % (DOMAIN) spotifyClient:SpotifyClient = await hass.async_add_executor_job( SpotifyClient, None, # manager:PoolManager=None, tokenStorageDir, # tokenStorageDir:str=None, tokenStorageFile, # tokenStorageFile:str=None, _TokenUpdater, # tokenUpdater:Callable=None, zeroconf_instance, # zeroconfClient:Zeroconf=None, entry.options.get(CONF_OPTION_DEVICE_USERNAME, None), # spotifyConnectUsername:str=None, entry.options.get(CONF_OPTION_DEVICE_PASSWORD, None), # spotifyConnectPassword:str=None, entry.options.get(CONF_OPTION_DEVICE_LOGINID, None), # spotifyConnectLoginId:str=None, 2.0, # spotifyConnectDiscoveryTimeout:float=2.0, # 0 to disable Spotify Connect Zeroconf browsing features. True, # spotifyConnectDirectoryEnabled:bool=True, # enable Spotify Connect Directory Task. None, # spotifyWebPlayerCookieSpdc:str=None, None, # spotifyWebPlayerCookieSpdc:str=None, ) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry spotifyClient object" % entry.title, spotifyClient) # set spotify web api python token authorization from HA-managed OAuth2 session token. tokenProfileId:str = entry.data.get(CONF_ID) _logsi.LogVerbose("'%s': Component async_setup_entry is setting SpotifyClient AuthToken from OAuth2 session token" % entry.title) await hass.async_add_executor_job( spotifyClient.SetAuthTokenFromToken, implementation.client_id, session.token, tokenProfileId ) # trace. _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry spotifyClient object (with AuthToken)" % entry.title, spotifyClient) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry Spotify UserProfile object" % entry.title, spotifyClient.UserProfile) # create runtime_data dictionary. runtime_data:dict = {} # ----------------------------------------------------------------------------------- # Method called when Home Assistant STOP event is detected. # ----------------------------------------------------------------------------------- async def handle_ha_stop_event(event): # trace. _logsi.LogVerbose("'%s': ha_stop_event was detected" % entry.title) # dispose of SpotifyClient resources (stops directory task, unwires events, etc). if (spotifyClient is not None): _logsi.LogVerbose("'%s': Component handle_ha_stop_event is disposing the SpotifyClient object" % entry.title) await hass.async_add_executor_job( spotifyClient.Dispose ) # hook into the HA stop event, so that we can cleanup resources properly. # we add this here, as the `async_unload_entry` method does not get called # when HA is stopped (or restarted). we want to make sure Dispose is called # so that PlayerLastPlayedInfo is stored to disk. # we will also save the returned value so that we can cancel the event handler # if the `async_unload_entry` method is called (on integration reload). _logsi.LogVerbose("'%s': Component async_setup_entry is registering handle_ha_stop_event task" % entry.title) unsubscribe_event_ha_stop = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, handle_ha_stop_event) runtime_data["unsubscribe_event_ha_stop"] = unsubscribe_event_ha_stop # create media player entity platform instance data. _logsi.LogVerbose("'%s': Component async_setup_entry is creating the media player platform instance data object" % entry.title) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = InstanceDataSpotifyPlus( session=session, spotifyClient=spotifyClient, media_player=None, options=entry.options, tokenUpdater_lock=TOKENUPDATER_LOCK, runtime_data=runtime_data, ) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_setup_entry media player platform instance data object" % entry.title, hass.data[DOMAIN][entry.entry_id]) # ensure authentication token scopes have not changed. if not set(session.token["scope"].split(" ")).issuperset(SPOTIFY_SCOPES): _logsi.LogWarning("'%s': Spotify authentication token scopes have changed; user needs to re-authenticate their application credentials" % (entry.title), colorValue=SIColors.Tan) raise ConfigEntryAuthFailed # TEST TODO - force application credentials to re-authenticate for the specified test account. # leave the following comments in here, in case you need to application credential re-authorization logic. # global REAUTH_TEST_FIRST_TIME # if (entry.title.lower().find("free") > -1) and (REAUTH_TEST_FIRST_TIME is None): # _logsi.LogWarning("'%s': TEST TODO - Testing app creds reauth processing" % (entry.title), colorValue=SIColors.Red) # REAUTH_TEST_FIRST_TIME = True # entry.async_start_reauth(hass) # start reauth flow. # dump initial Spotify Connect device list (for HA debug log). scDevices:SpotifyConnectDevices = spotifyClient.SpotifyConnectDirectory.GetDevices() scDevice:SpotifyConnectDevice _logsi.LogVerbose("'%s': Spotify Connect devices discovered by Spotify Connect Directory task (%s items)" % (entry.title, scDevices.ItemsCount)) for scDevice in scDevices: isActive:str = " (active)" if (scDevice.IsActiveDevice) else "" _logsi.LogVerbose("'%s': Spotify Connect device: %s [%s]%s" % (entry.title, scDevice.Title, scDevice.DiscoveryResult.Description, isActive)) # we are now ready for HA to create individual objects for each platform that # our device requires; in our case, it's just a media_player platform. # we initiate this by calling the `async_forward_entry_setups`, which # calls the `async_setup_entry` function in each platform module (e.g. # media_player.py) for each device instance. _logsi.LogVerbose("'%s': Component async_setup_entry is forwarding configuration entry setups to create the individual media player platforms" % entry.title) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) # register an update listener to reload configuration entry when options are updated. # this will return an "unlisten" function, which will be added to the configuration # "on_unload" event handler to automatically unregister the update listener when # the configuration is unloaded. listenerRemovePtr = entry.add_update_listener(options_update_listener) _logsi.LogArray(SILevel.Verbose, "'%s': Component update listener has been registered and added to update listeners array (%d array items)" % (entry.title, len(entry.update_listeners)), entry.update_listeners) entry.async_on_unload(listenerRemovePtr) _logsi.LogArray(SILevel.Verbose, "'%s': Component update listener auto-unregister method has been added to on_unload event handlers array (%d array items)" % (entry.title, len(entry._on_unload)), entry._on_unload) # trace. _logsi.LogVerbose("'%s': Component async_setup_entry is complete" % entry.title) # indicate success. return True except Exception as ex: # this is usually caused by a temporary error (e.g. device unplugged, network connectivity, etc), in # which case the user will need to manually reload the device when the temporary condition is cleared. # if it's a permanent error (e.g. ip address change), then the user needs to correct the configuration. # trace. _logsi.LogException("'%s': Component async_setup_entry exception" % entry.title, ex, logToSystemLogger=False) # reset storage. spotifyClient = None # inform HA that the configuration is not ready. raise ConfigEntryNotReady from ex finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def async_unload_entry(hass:HomeAssistant, entry:ConfigEntry) -> bool: """ Unloads a configuration entry. Args: hass (HomeAssistant): HomeAssistant instance. entry (ConfigEntry): HomeAssistant configuration entry object. The __init__.py module "async_unload_entry" unloads a configuration entry. This method is called when a configuration entry is to be removed. The class needs to unload itself, and remove any callbacks. Note that any options update listeners (added via "add_update_listener") do not need to be removed, as they are already removed by the time this method is called. This is accomplished by the "entry.async_on_unload(listener)" call in async_setup_entry, which removes them from the configuration entry just before it is unloaded. Note that something changed with HA 2024.6 release that causes the `update_listeners` array to still contain entries; prior to this release, the `update_listeners` array was empty by this point. """ try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_unload_entry configuration entry" % entry.title, entry) # unload any platforms this device supports. _logsi.LogVerbose("'%s':Component async_unload_entry is unloading our device instance from the domain" % entry.title) unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) # if unload was successful, then remove data associated with the device. if unload_ok: # remove instance data from domain. _logsi.LogVerbose("'%s': Component async_unload_entry is removing our device instance data from the domain" % entry.title) data:InstanceDataSpotifyPlus = hass.data[DOMAIN].pop(entry.entry_id) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_unload_entry unloaded configuration entry instance data" % entry.title, data) # dispose of SpotifyClient resources (stops directory task, unwires events, etc). if (data is not None): if (data.spotifyClient is not None): # dispose of the spotifyClient object. _logsi.LogVerbose("'%s': Component async_unload_entry is disposing the SpotifyClient object" % entry.title) await hass.async_add_executor_job( data.spotifyClient.Dispose ) # if we tied into the HA stop event, then cancel it since we are handling it here. # if we don't cancel it here, then it will try to Dispose again in the HA stop event! unsubscribe_event_ha_stop = data.runtime_data.get("unsubscribe_event_ha_stop") if unsubscribe_event_ha_stop: _logsi.LogVerbose("'%s': Component async_unload_entry is deregistering handle_ha_stop_event task" % entry.title) unsubscribe_event_ha_stop() # a quick check to make sure all update listeners were removed (see method doc notes above). if len(entry.update_listeners) > 0: _logsi.LogArray(SILevel.Warning, "'%s': Component configuration update_listener(s) did not get removed before configuration unload (%d items - should be 0 prior to HA 2026.0 release, but after that release still contains entries)" % (entry.title, len(entry.update_listeners)), entry.update_listeners) # 2024/06/08 - I commented out the following line to clear the `update_listeners`, as it was causing `ValueError: list.remove(x): x not in list` # exceptions starting with the HA 2024.6.0 release! #entry.update_listeners.clear() # return status to caller. _logsi.LogVerbose("'%s': Component async_unload_entry completed" % entry.title) return unload_ok finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def async_reload_entry(hass:HomeAssistant, entry:ConfigEntry) -> None: """ Reload config entry. Args: hass (HomeAssistant): HomeAssistant instance. entry (ConfigEntry): HomeAssistant configuration entry object. The __init__.py module "async_reload_entry" reloads a configuration entry. This method is called when an entry/configured device is to be reloaded. The class needs to unload itself, remove callbacks, and call async_setup_entry. """ try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogObject(SILevel.Verbose, "'%s': Component async_reload_entry configuration entry" % entry.title, entry) # unload the configuration entry. _logsi.LogVerbose("'%s': Component async_reload_entry is unloading the configuration entry" % entry.title) await async_unload_entry(hass, entry) # reload (setup) the configuration entry. _logsi.LogVerbose("'%s': Component async_reload_entry is reloading the configuration entry" % entry.title) await async_setup_entry(hass, entry) # trace. _logsi.LogVerbose("'%s': Component async_reload_entry completed" % entry.title) finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def options_update_listener(hass:HomeAssistant, entry:ConfigEntry) -> None: """ Configuration entry update event handler. Args: hass (HomeAssistant): HomeAssistant instance. entry (ConfigEntry): HomeAssistant configuration entry object. Reloads the config entry after updates have been applied to a configuration entry. This method is called when the configuration has been updated via any of the following methods: - user initiates a configuration change via the HA UI (e.g. options update, etc). in this scenario, we will reload the configuration to apply the updated settings. - when a call is made to async_update_entry with changed configuration data. in this scenario, we will reload the configuration to apply the updated settings. - authentication token is refreshed. in this scenario, we do NOT want to reload the configuration since options have not changed; it's just the authentication token that is being udpated, which will occur every 1 hour (controlled by Spotify Web API token expiration). """ try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogObject(SILevel.Verbose, "'%s': Component detected configuration entry options update" % entry.title, entry) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component options_update_listener entry.data dictionary" % entry.title, entry.data) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component options_update_listener entry.options dictionary" % entry.title, entry.options) # check if the authentication token was refreshed; if not, then it's a user-initiated # change and the configuration should be reloaded to apply the changes. # if it IS an authentication token refresh, then do NOT reload the configuration. shouldReload:bool = True _logsi.LogVerbose("'%s': Component options_update_listener is checking for authentication token refresh event" % entry.title) if (entry.data is not None): token:dict = entry.data.get('token', None) if (token is not None): _logsi.LogDictionary(SILevel.Verbose, "'%s': Component options_update_listener token data" % entry.title, token) status = token.get(TOKEN_STATUS, None) if (status == TOKEN_STATUS_REFRESH_EVENT): # token refresh detected; indicate configuration should not be reloaded, and remove # the token status key so it's not saved with the configuration data. shouldReload = False entry.data['token'].pop(TOKEN_STATUS, None) _logsi.LogVerbose("'%s': Component options_update_listener detected authentication token refresh; configuration will NOT be reloaded" % entry.title) # reload the configuration entry (if necessary). if shouldReload: # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is preparing to set the lock for method options_update_listener" % entry.title, colorValue=SIColors.Gold) if TOKENUPDATER_LOCK.locked(): _logsi.LogVerbose("'%s': options_update_listener is waiting on TOKENUPDATER_LOCK to free" % entry.title, colorValue=SIColors.Gold) try: # only allow one thread to update authorization token at a time; otherwise a deadlock # occurs in the `run_coroutine_threadsafe(session.implementation.async_refresh_token)` line! with TOKENUPDATER_LOCK: # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is set for method options_update_listener" % entry.title, colorValue=SIColors.Gold) _logsi.LogVerbose("'%s': Component options_update_listener is reloading the configuration (due to UI options change)" % entry.title) await hass.config_entries.async_reload(entry.entry_id) finally: # trace. _logsi.LogVerbose("'%s': TOKENUPDATER_LOCK is RESET for method options_update_listener" % entry.title, colorValue=SIColors.Gold) else: # trace. _logsi.LogVerbose("'%s': Component options_update_listener is NOT reloading the configuration (due to token refresh)" % entry.title) # trace. _logsi.LogVerbose("'%s': Component options_update_listener completed" % entry.title) finally: # trace. _logsi.LeaveMethod(SILevel.Debug) async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """ Migrate older version of a config entry to a new version format. You can also update the `unique_id` value as well. This method is called when the configuration entry.version value does not match the hardcoded `SpotifyPlusConfigFlow.VERSION = n` value. """ try: # trace. _logsi.EnterMethod(SILevel.Debug) _logsi.LogObject(SILevel.Verbose, "'%s': Component detected configuration entry migration" % entry.title, entry) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_migrate_entry entry.data dictionary" % entry.title, entry.data) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_migrate_entry entry.options dictionary" % entry.title, entry.options) # check config entry version to know what we need to migrate from. if entry.version == 1: # trace. _logsi.LogVerbose("'%s': Migrating config entry from version %s to version 2" % (entry.title, entry.version), colorValue=SIColors.DarkBlue) # as of HA 2026.03 the unique_id value needs to be unique across all domains, # otherwise an exception will occur and the integration will not load! # version 1 of the integration uses the same unique_id as the HA Spotify # integration (e.g. Spotify user id), so we need to update the unique_id value. # in our case, we will just tack the "_DOMAIN" suffix onto the existing unique_id. # save the current unique_id value for entity entry modifications. old_unique_id = entry.unique_id # formulate the new unique_id value. new_unique_id = entry.unique_id + "_" + DOMAIN _logsi.LogVerbose("'%s': Migrating config entry unique_id from \"%s\" to \"%s\"" % (entry.title, entry.unique_id, new_unique_id), colorValue=SIColors.DarkBlue) # if you also need to adjust data. new_data = {**entry.data} # if "deprecated_key" in new_data: # new_data.pop("deprecated_key") # if you also need to adjust options. new_options = {**entry.options} # if "deprecated_key" in new_options: # new_options.pop("deprecated_key") # update configuration entry to newer version structure, and # increment the version indicator by one. hass.config_entries.async_update_entry( entry, unique_id=new_unique_id, data=new_data, options=new_options, version=2, # update version to next version number ) # now that the configuration entry has been updated, let's loop through # the entity registry and update the unique id of any existing entities # that are children of this configuration entry. # get the entity registry. entity_registry = er.async_get(hass) # process all entities. for entity in list(entity_registry.entities.values()): # is this entity a child of the configuration entry? if not, then don't bother. if entity.config_entry_id != entry.entry_id: continue # is this entity referencing the config entry old unique_id value? if entity.unique_id == old_unique_id: # is there an entity that already contains our new unique_id? if entity_registry.async_get_entity_id(entity.domain, entity.platform, new_unique_id): # duplicate found; remove the old entity. _logsi.LogVerbose("'%s': Removing existing entity_id \"%s\" so that a new entity can be created for the unique_id (avoid duplicate entities)" % (entry.title, entity.entity_id), colorValue=SIColors.DarkBlue) entity_registry.async_remove(entity.entity_id) else: # update the entity with the new unique_id. _logsi.LogVerbose("'%s': Updating entity_id \"%s\" unique_id value from \"%s\" to \"%s\"" % (entry.title, entity.entity_id, entity.unique_id, new_unique_id), colorValue=SIColors.DarkBlue) entity_registry.async_update_entity(entity.entity_id, new_unique_id=new_unique_id) # trace. _logsi.LogObject(SILevel.Verbose, "'%s': Component configuration entry migration complete" % entry.title, entry) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_migrate_entry entry.data dictionary (migrated)" % entry.title, entry.data) _logsi.LogDictionary(SILevel.Verbose, "'%s': Component async_migrate_entry entry.options dictionary (migrated)" % entry.title, entry.options) # return True so Home Assistant knows the migration succeeded. return True except Exception as ex: # log exception, but not to system logger as HA will take care of it. _logsi.LogException("'%s': Component async_migrate_entry Exception" % (entry.title), ex, logToSystemLogger=False) raise finally: # trace. _logsi.LeaveMethod(SILevel.Debug)