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

615 lines
26 KiB
Python

import voluptuous as vol
from hassil.intents import (
TextSlotValue,
)
from homeassistant.components.media_player.const import (
ATTR_MEDIA_ALBUM_NAME,
ATTR_MEDIA_ARTIST,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_TITLE,
)
from homeassistant.const import (
STATE_PAUSED,
STATE_PLAYING,
)
from homeassistant.core import State, ServiceResponse
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.intent import (
Intent,
IntentResponse,
IntentResponseErrorCode,
)
from smartinspectpython.siauto import SILevel, SIColors
from spotifywebapipython import SpotifyMediaTypes
from ..appmessages import STAppMessages
from ..intent_loader import IntentLoader, LanguageIntents
from ..utils import get_id_from_uri
from ..const import (
ATTR_SPOTIFYPLUS_ARTIST_URI,
ATTR_SPOTIFYPLUS_CONTEXT_URI,
ATTR_SPOTIFYPLUS_ITEM_TYPE,
ATTR_SPOTIFYPLUS_PLAYLIST_NAME,
ATTR_SPOTIFYPLUS_PLAYLIST_URI,
ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN,
CONF_TEXT,
CONF_VALUE,
DOMAIN,
INTENT_GET_NOWPLAYING_INFO,
PLATFORM_SPOTIFYPLUS,
RESPONSE_ERROR_MEDIA_TYPE_INVALID,
RESPONSE_GET_INFO_ARTIST_BIO,
RESPONSE_NOWPLAYING_INFO_ALBUM,
RESPONSE_NOWPLAYING_INFO_AUDIOBOOK,
RESPONSE_NOWPLAYING_INFO_PLAYLIST,
RESPONSE_NOWPLAYING_INFO_PODCAST,
RESPONSE_NOWPLAYING_INFO_PODCAST_EPISODE,
RESPONSE_NOWPLAYING_INFO_TRACK,
RESPONSE_SPOTIFY_NO_ARTIST_INFO,
RESPONSE_NOWPLAYING_NO_MEDIA_ARTIST,
RESPONSE_NOWPLAYING_NO_MEDIA_AUDIOBOOK,
RESPONSE_NOWPLAYING_NO_MEDIA_PLAYLIST,
RESPONSE_NOWPLAYING_NO_MEDIA_PODCAST,
RESPONSE_NOWPLAYING_NO_MEDIA_PODCAST_EPISODE,
RESPONSE_NOWPLAYING_NO_MEDIA_TRACK,
RESPONSE_PLAYER_NOT_PLAYING_MEDIA,
SERVICE_SPOTIFY_GET_ARTIST_INFO,
SERVICE_SPOTIFY_GET_TRACK,
SLOT_AREA,
SLOT_ALBUM_TITLE,
SLOT_ALBUM_URL,
SLOT_ARTIST_BIO,
SLOT_ARTIST_TITLE,
SLOT_ARTIST_URL,
SLOT_AUDIOBOOK_TITLE,
SLOT_AUDIOBOOK_URL,
SLOT_AUTHOR_TITLE,
SLOT_CHAPTER_TITLE,
SLOT_EPISODE_TITLE,
SLOT_EPISODE_URL,
SLOT_FLOOR,
SLOT_SPOTIFYPLUS_PLAYLIST_NAMES,
SLOT_NAME,
SLOT_PLAYLIST_TITLE,
SLOT_PLAYLIST_URL,
SLOT_PODCAST_TITLE,
SLOT_PODCAST_URL,
SLOT_PREFERRED_AREA_ID,
SLOT_PREFERRED_FLOOR_ID,
SLOT_SPOTIFYPLUS_MEDIA_TYPE,
SLOT_TRACK_TITLE,
SLOT_TRACK_URL,
SPOTIFY_WEB_URL_PFX,
)
from .spotifyplusintenthandler import SpotifyPlusIntentHandler
class SpotifyPlusGetNowPlayingInfo_Handler(SpotifyPlusIntentHandler):
"""
Handles intents for SpotifyPlusGetNowPlayingInfo.
"""
def __init__(self, intentLoader:IntentLoader) -> None:
"""
Initializes a new instance of the IntentHandler class.
"""
# invoke base class method.
super().__init__(intentLoader)
# set intent handler basics.
self.description = "Queries media player state for now playing context information."
self.intent_type = INTENT_GET_NOWPLAYING_INFO
self.platforms = {PLATFORM_SPOTIFYPLUS}
@property
def slot_schema(self) -> dict | None:
"""
Returns the slot schema for this intent.
"""
return {
# slots that determine which media player entity will be used.
vol.Optional(SLOT_NAME): cv.string,
vol.Optional(SLOT_AREA): cv.string,
vol.Optional(SLOT_FLOOR): cv.string,
vol.Optional(SLOT_PREFERRED_AREA_ID): cv.string,
vol.Optional(SLOT_PREFERRED_FLOOR_ID): cv.string,
# slots for other service arguments.
vol.Optional(SLOT_SPOTIFYPLUS_MEDIA_TYPE): cv.string,
}
async def async_HandleIntent(
self,
intentObj: Intent,
intentResponse: IntentResponse
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
Returns:
An IntentResponse object.
"""
# invoke base class method to resolve the player entity and its state.
playerEntityState:State = await super().async_GetMatchingPlayerState(
intentObj,
intentResponse,
desiredFeatures=None,
desiredStates=[STATE_PLAYING, STATE_PAUSED],
desiredStateResponseKey=RESPONSE_PLAYER_NOT_PLAYING_MEDIA,
)
# if media player was not resolved, then we are done;
# note that the base class method above already called `async_set_speech` with a response.
if playerEntityState is None:
return intentResponse
# get optional arguments (if provided).
media_type:str = intentObj.slots.get(SLOT_SPOTIFYPLUS_MEDIA_TYPE, {}).get(CONF_VALUE, "").lower()
# process based on media type.
if (media_type == SpotifyMediaTypes.ALBUM.value):
return await self.async_ProcessAlbum(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.ARTIST.value):
return await self.async_ProcessArtist(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.AUDIOBOOK.value):
return await self.async_ProcessAudiobook(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.PLAYLIST.value):
return await self.async_ProcessPlaylist(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.PODCAST.value):
return await self.async_ProcessPodcast(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.EPISODE.value):
return await self.async_ProcessPodcastEpisode(intentObj, intentResponse, playerEntityState)
elif (media_type == SpotifyMediaTypes.TRACK.value):
return await self.async_ProcessTrack(intentObj, intentResponse, playerEntityState)
else:
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_ERROR_MEDIA_TYPE_INVALID, IntentResponseErrorCode.FAILED_TO_HANDLE)
async def async_ProcessAlbum(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a track? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.TRACK.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_TRACK)
# get now playing details.
album_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
artist_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
artist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ARTIST_URI)
track_name:str = playerEntityState.attributes.get(ATTR_MEDIA_TITLE)
track_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN)
# get id portion of spotify uri value.
artist_id:str = get_id_from_uri(artist_uri)
track_id:str = get_id_from_uri(track_uri)
# update slots with returned info.
intentObj.slots[SLOT_ARTIST_TITLE] = { CONF_VALUE: artist_uri, CONF_TEXT: artist_name }
intentObj.slots[SLOT_ARTIST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.ARTIST.value}/{artist_id}", CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_TRACK_TITLE] = { CONF_VALUE: track_uri, CONF_TEXT: track_name }
intentObj.slots[SLOT_TRACK_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.TRACK.value}/{track_id}", CONF_TEXT: "Spotify" }
# trace.
if (self.logsi.IsOn(SILevel.Verbose)):
self.logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_INTENT_HANDLER_SLOT_INFO % intentObj.intent_type, intentObj.slots, colorValue=SIColors.Khaki)
# set service name and build parameters.
svcName:str = SERVICE_SPOTIFY_GET_TRACK
svcData:dict = \
{
"entity_id": playerEntityState.entity_id,
"track_id": track_id,
}
# call integration service for this intent.
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
info_result:ServiceResponse = await intentObj.hass.services.async_call(
DOMAIN,
svcName,
svcData,
blocking=True,
context=intentObj.context,
return_response=True,
)
self.logsi.LogDictionary(SILevel.Verbose, "SERVICE_SPOTIFY_GET_TRACK result", info_result, prettyPrint=True, colorValue=SIColors.Khaki)
# get related details and update slot info.
album_uri:str = info_result.get("result",{}).get("album",{}).get("uri",None)
album_url:str = info_result.get("result",{}).get("album",{}).get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
artist_url:str = info_result.get("result",{}).get("artists",[])[0].get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
track_url:str = info_result.get("result",{}).get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
# update slots with returned info.
intentObj.slots[SLOT_ALBUM_TITLE] = { CONF_VALUE: album_uri, CONF_TEXT: album_name }
intentObj.slots[SLOT_ALBUM_URL] = { CONF_VALUE: album_url, CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_ARTIST_URL] = { CONF_VALUE: artist_url, CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_TRACK_URL] = { CONF_VALUE: track_url, CONF_TEXT: "Spotify" }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_ALBUM)
async def async_ProcessArtist(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a track? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.TRACK.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_ARTIST)
# get now playing details.
artist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ARTIST_URI)
artist_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
# get id portion of spotify uri value.
artist_id:str = get_id_from_uri(artist_uri)
# update slots with returned info.
intentObj.slots[SLOT_ARTIST_TITLE] = { CONF_VALUE: artist_uri, CONF_TEXT: artist_name }
intentObj.slots[SLOT_ARTIST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.ARTIST.value}/{artist_id}", CONF_TEXT: "Spotify" }
# trace.
if (self.logsi.IsOn(SILevel.Verbose)):
self.logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_INTENT_HANDLER_SLOT_INFO % intentObj.intent_type, intentObj.slots, colorValue=SIColors.Khaki)
# set service name and build parameters.
svcName:str = SERVICE_SPOTIFY_GET_ARTIST_INFO
svcData:dict = \
{
"entity_id": playerEntityState.entity_id,
"artist_id": artist_id,
}
# call integration service for this intent.
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
info_result:ServiceResponse = await intentObj.hass.services.async_call(
DOMAIN,
svcName,
svcData,
blocking=True,
context=intentObj.context,
return_response=True,
)
self.logsi.LogDictionary(SILevel.Verbose, "SERVICE_SPOTIFY_GET_ARTIST_INFO result", info_result, prettyPrint=True, colorValue=SIColors.Khaki)
# get artist bio info and update slot info.
artist_bio:str = info_result.get("result",{}).get("bio", None)
# if no artist info found, then we are done.
if (artist_bio is None):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_SPOTIFY_NO_ARTIST_INFO)
# update slots with returned info.
intentObj.slots[SLOT_ARTIST_BIO] = { CONF_VALUE: "", CONF_TEXT: artist_bio }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_GET_INFO_ARTIST_BIO)
async def async_ProcessAudiobook(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item an audiobook? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.AUDIOBOOK.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_AUDIOBOOK)
# get now playing details.
audiobook_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
audiobook_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
author_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
chapter_uri:str = playerEntityState.attributes.get(ATTR_MEDIA_CONTENT_ID)
chapter_name:str = playerEntityState.attributes.get(ATTR_MEDIA_TITLE)
# get id portion of spotify uri value.
audiobook_id:str = get_id_from_uri(audiobook_uri)
# update slots with returned info.
intentObj.slots[SLOT_AUDIOBOOK_TITLE] = { CONF_VALUE: audiobook_uri, CONF_TEXT: audiobook_name }
intentObj.slots[SLOT_AUDIOBOOK_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.SHOW.value}/{audiobook_id}", CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_AUTHOR_TITLE] = { CONF_VALUE: "", CONF_TEXT: author_name }
intentObj.slots[SLOT_CHAPTER_TITLE] = { CONF_VALUE: chapter_uri, CONF_TEXT: chapter_name }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_AUDIOBOOK)
async def async_ProcessPlaylist(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a playlist (e.g. spotify:playlist:x)? if not, then we are done.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
if (item_type is None) or (item_type.find(SpotifyMediaTypes.PLAYLIST.value) == -1):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_PLAYLIST)
# get now playing details.
playlist_name:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_PLAYLIST_NAME)
playlist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_PLAYLIST_URI)
# if playlist name is "unknown", then it's probably a Spotify generated list; if so
# then we will indicate this to the user. Note that the favorite will be shown in
# the Spotify Favorites with the correct name (e.g. "Daily Mix 01").
if ((playlist_name or "").lower() == "unknown"):
# check the "spotifyplus_playlist_name" list for a matching uri value.
playlist_name = await self.GetTextSlotListInValue(
intentObj,
SLOT_SPOTIFYPLUS_PLAYLIST_NAMES,
playlist_uri,
"Spotify Algorithmic Playlist"
)
# get id portion of spotify uri value.
playlist_id:str = get_id_from_uri(playlist_uri)
# update slots with returned info.
intentObj.slots[SLOT_PLAYLIST_TITLE] = { CONF_VALUE: playlist_uri, CONF_TEXT: playlist_name }
intentObj.slots[SLOT_PLAYLIST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.PLAYLIST.value}/{playlist_id}", CONF_TEXT: "Spotify" }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_PLAYLIST)
async def async_ProcessPodcast(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a podcast episode? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.PODCAST.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_PODCAST)
# get now playing details.
podcast_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
podcast_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
episode_name:str = playerEntityState.attributes.get(ATTR_MEDIA_TITLE)
episode_uri:str = playerEntityState.attributes.get(ATTR_MEDIA_CONTENT_ID)
# get id portion of spotify uri value.
podcast_id:str = get_id_from_uri(podcast_uri)
episode_id:str = get_id_from_uri(episode_uri)
# update slots with returned info.
intentObj.slots[SLOT_PODCAST_TITLE] = { CONF_VALUE: podcast_uri, CONF_TEXT: podcast_name }
intentObj.slots[SLOT_PODCAST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.SHOW.value}/{podcast_id}", CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_EPISODE_TITLE] = { CONF_VALUE: episode_uri, CONF_TEXT: episode_name }
intentObj.slots[SLOT_EPISODE_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.EPISODE.value}/{episode_id}", CONF_TEXT: "Spotify" }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_PODCAST)
async def async_ProcessPodcastEpisode(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a podcast episode? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.PODCAST.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_PODCAST_EPISODE)
# get now playing details.
podcast_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
podcast_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
episode_name:str = playerEntityState.attributes.get(ATTR_MEDIA_TITLE)
episode_uri:str = playerEntityState.attributes.get(ATTR_MEDIA_CONTENT_ID)
# get id portion of spotify uri value.
podcast_id:str = get_id_from_uri(podcast_uri)
episode_id:str = get_id_from_uri(episode_uri)
# update slots with returned info.
intentObj.slots[SLOT_PODCAST_TITLE] = { CONF_VALUE: podcast_uri, CONF_TEXT: podcast_name }
intentObj.slots[SLOT_PODCAST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.SHOW.value}/{podcast_id}", CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_EPISODE_TITLE] = { CONF_VALUE: episode_uri, CONF_TEXT: episode_name }
intentObj.slots[SLOT_EPISODE_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.EPISODE.value}/{episode_id}", CONF_TEXT: "Spotify" }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_PODCAST_EPISODE)
async def async_ProcessTrack(
self,
intentObj: Intent,
intentResponse: IntentResponse,
playerEntityState: State,
) -> IntentResponse:
"""
Handles the intent.
Args:
intentObj (Intent):
Intent object.
intentResponse (IntentResponse)
Intent response object.
playerEntityState (State):
Target player entity state.
Returns:
An IntentResponse object.
"""
# is now playing item a track? if not, then don't bother.
item_type:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ITEM_TYPE)
if (item_type != SpotifyMediaTypes.TRACK.value):
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_NO_MEDIA_TRACK)
# get now playing details.
album_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
artist_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
artist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ARTIST_URI)
track_name:str = playerEntityState.attributes.get(ATTR_MEDIA_TITLE)
track_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_TRACK_URI_ORIGIN)
# get id portion of spotify uri value.
artist_id:str = get_id_from_uri(artist_uri)
track_id:str = get_id_from_uri(track_uri)
# update slots with returned info.
intentObj.slots[SLOT_ARTIST_TITLE] = { CONF_VALUE: artist_uri, CONF_TEXT: artist_name }
intentObj.slots[SLOT_ARTIST_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.ARTIST.value}/{artist_id}", CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_TRACK_TITLE] = { CONF_VALUE: track_uri, CONF_TEXT: track_name }
intentObj.slots[SLOT_TRACK_URL] = { CONF_VALUE: f"{SPOTIFY_WEB_URL_PFX}/{SpotifyMediaTypes.TRACK.value}/{track_id}", CONF_TEXT: "Spotify" }
# trace.
if (self.logsi.IsOn(SILevel.Verbose)):
self.logsi.LogDictionary(SILevel.Verbose, STAppMessages.MSG_INTENT_HANDLER_SLOT_INFO % intentObj.intent_type, intentObj.slots, colorValue=SIColors.Khaki)
# set service name and build parameters.
svcName:str = SERVICE_SPOTIFY_GET_TRACK
svcData:dict = \
{
"entity_id": playerEntityState.entity_id,
"track_id": track_id,
}
# call integration service for this intent.
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
info_result:ServiceResponse = await intentObj.hass.services.async_call(
DOMAIN,
svcName,
svcData,
blocking=True,
context=intentObj.context,
return_response=True,
)
self.logsi.LogDictionary(SILevel.Verbose, "SERVICE_SPOTIFY_GET_TRACK result", info_result, prettyPrint=True, colorValue=SIColors.Khaki)
# get related details and update slot info.
album_uri:str = info_result.get("result",{}).get("album",{}).get("uri",None)
album_url:str = info_result.get("result",{}).get("album",{}).get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
artist_url:str = info_result.get("result",{}).get("artists",[])[0].get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
track_url:str = info_result.get("result",{}).get("external_urls", {}).get("spotify", SPOTIFY_WEB_URL_PFX)
# update slots with returned info.
intentObj.slots[SLOT_ALBUM_TITLE] = { CONF_VALUE: album_uri, CONF_TEXT: album_name }
intentObj.slots[SLOT_ALBUM_URL] = { CONF_VALUE: album_url, CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_ARTIST_URL] = { CONF_VALUE: artist_url, CONF_TEXT: "Spotify" }
intentObj.slots[SLOT_TRACK_URL] = { CONF_VALUE: track_url, CONF_TEXT: "Spotify" }
# return intent response.
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_NOWPLAYING_INFO_TRACK)