Initial Home Assistant commit
This commit is contained in:
+760
@@ -0,0 +1,760 @@
|
||||
import voluptuous as vol
|
||||
|
||||
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
|
||||
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
|
||||
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_ADD,
|
||||
CONF_REMOVE,
|
||||
CONF_TEXT,
|
||||
CONF_VALUE,
|
||||
DOMAIN,
|
||||
INTENT_FAVORITE_ADD_REMOVE,
|
||||
PLATFORM_SPOTIFYPLUS,
|
||||
RESPONSE_ERROR_MEDIA_TYPE_INVALID,
|
||||
RESPONSE_FAVORITE_OPERATION_INVALID,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_ALBUM_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_ARTIST_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_AUDIOBOOK_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_PLAYLIST_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_PODCAST_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_PODCAST_EPISODE_OK,
|
||||
RESPONSE_FAVORITE_ADD_REMOVE_TRACK_OK,
|
||||
RESPONSE_NOWPLAYING_NO_MEDIA_ALBUM,
|
||||
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_SAVE_ALBUM_FAVORITES,
|
||||
SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES,
|
||||
SERVICE_SPOTIFY_FOLLOW_ARTISTS,
|
||||
SERVICE_SPOTIFY_UNFOLLOW_ARTISTS,
|
||||
SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES,
|
||||
SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES,
|
||||
SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES,
|
||||
SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES,
|
||||
SERVICE_SPOTIFY_FOLLOW_PLAYLIST,
|
||||
SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST,
|
||||
SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES,
|
||||
SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES,
|
||||
SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES,
|
||||
SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES,
|
||||
SLOT_AREA,
|
||||
SLOT_ALBUM_TITLE,
|
||||
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_FAVORITE_OPERATOR,
|
||||
SLOT_FLOOR,
|
||||
SLOT_IS_PUBLIC,
|
||||
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 SpotifyPlusFavoriteAddRemove_Handler(SpotifyPlusIntentHandler):
|
||||
"""
|
||||
Handles intents for SpotifyPlusFavoriteAddRemove.
|
||||
"""
|
||||
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 = "Add / Remove the currently playing context type to / from Spotify user favorites."
|
||||
self.intent_type = INTENT_FAVORITE_ADD_REMOVE
|
||||
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_FAVORITE_OPERATOR): cv.string,
|
||||
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).
|
||||
favorite_operator:str = intentObj.slots.get(SLOT_FAVORITE_OPERATOR, {}).get(CONF_VALUE, "").lower()
|
||||
media_type:str = intentObj.slots.get(SLOT_SPOTIFYPLUS_MEDIA_TYPE, {}).get(CONF_VALUE, "").lower()
|
||||
|
||||
# validations.
|
||||
if (favorite_operator not in [CONF_ADD, CONF_REMOVE]):
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_OPERATION_INVALID, IntentResponseErrorCode.FAILED_TO_HANDLE)
|
||||
|
||||
# process based on media type.
|
||||
if (media_type == SpotifyMediaTypes.ALBUM.value):
|
||||
return await self.async_ProcessAlbum(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.ARTIST.value):
|
||||
return await self.async_ProcessArtist(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.AUDIOBOOK.value):
|
||||
return await self.async_ProcessAudiobook(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.PLAYLIST.value):
|
||||
return await self.async_ProcessPlaylist(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.PODCAST.value):
|
||||
return await self.async_ProcessPodcast(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.EPISODE.value):
|
||||
return await self.async_ProcessPodcastEpisode(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
elif (media_type == SpotifyMediaTypes.TRACK.value):
|
||||
return await self.async_ProcessTrack(intentObj, intentResponse, playerEntityState, favorite_operator)
|
||||
|
||||
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,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item a track? if not, then we are done.
|
||||
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_ALBUM)
|
||||
|
||||
# get now playing details.
|
||||
artist_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
|
||||
artist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ARTIST_URI)
|
||||
album_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
|
||||
|
||||
# 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" }
|
||||
intentObj.slots[SLOT_ALBUM_TITLE] = { CONF_VALUE: "", CONF_TEXT: album_name }
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_SAVE_ALBUM_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_REMOVE_ALBUM_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_ALBUM_OK)
|
||||
|
||||
|
||||
async def async_ProcessArtist(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item a track? if not, then we are done.
|
||||
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_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ARTIST)
|
||||
artist_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_ARTIST_URI)
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_FOLLOW_ARTISTS
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_UNFOLLOW_ARTISTS
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_ARTIST_OK)
|
||||
|
||||
|
||||
async def async_ProcessAudiobook(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item an audiobook? if not, then we are done.
|
||||
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 }
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_SAVE_AUDIOBOOK_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_REMOVE_AUDIOBOOK_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_AUDIOBOOK_OK)
|
||||
|
||||
|
||||
async def async_ProcessPlaylist(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# get optional arguments (if provided).
|
||||
is_public = intentObj.slots.get(SLOT_IS_PUBLIC, {}).get(CONF_VALUE, True)
|
||||
|
||||
# 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"):
|
||||
playlist_name = "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" }
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_FOLLOW_PLAYLIST
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
"public": is_public
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_UNFOLLOW_PLAYLIST
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_PLAYLIST_OK)
|
||||
|
||||
|
||||
async def async_ProcessPodcast(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item an podcast? if not, then we are done.
|
||||
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_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
|
||||
podcast_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
|
||||
|
||||
# get id portion of spotify uri value.
|
||||
podcast_id:str = get_id_from_uri(podcast_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" }
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_SAVE_SHOW_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_REMOVE_SHOW_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_PODCAST_OK)
|
||||
|
||||
|
||||
async def async_ProcessPodcastEpisode(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item an podcast? if not, then we are done.
|
||||
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_uri:str = playerEntityState.attributes.get(ATTR_SPOTIFYPLUS_CONTEXT_URI)
|
||||
podcast_name:str = playerEntityState.attributes.get(ATTR_MEDIA_ALBUM_NAME)
|
||||
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" }
|
||||
|
||||
# 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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_SAVE_EPISODE_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_REMOVE_EPISODE_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_PODCAST_EPISODE_OK)
|
||||
|
||||
|
||||
async def async_ProcessTrack(
|
||||
self,
|
||||
intentObj: Intent,
|
||||
intentResponse: IntentResponse,
|
||||
playerEntityState: State,
|
||||
favorite_operator: str,
|
||||
) -> IntentResponse:
|
||||
"""
|
||||
Handles the intent.
|
||||
|
||||
Args:
|
||||
intentObj (Intent):
|
||||
Intent object.
|
||||
intentResponse (IntentResponse)
|
||||
Intent response object.
|
||||
playerEntityState (State):
|
||||
Target player entity state.
|
||||
favorite_operator (str):
|
||||
Favorite operator (e.g. add, remove).
|
||||
|
||||
Returns:
|
||||
An IntentResponse object.
|
||||
"""
|
||||
# is now playing item a track? if not, then we are done.
|
||||
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.
|
||||
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)
|
||||
|
||||
# add / remove the favorite.
|
||||
if (favorite_operator == CONF_ADD):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_SAVE_TRACK_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
elif (favorite_operator == CONF_REMOVE):
|
||||
|
||||
# set service name and build parameters.
|
||||
svcName:str = SERVICE_SPOTIFY_REMOVE_TRACK_FAVORITES
|
||||
svcData:dict = \
|
||||
{
|
||||
"entity_id": playerEntityState.entity_id,
|
||||
}
|
||||
|
||||
# call integration service for this intent.
|
||||
self.logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (svcName, playerEntityState.entity_id), colorValue=SIColors.Khaki)
|
||||
await intentObj.hass.services.async_call(
|
||||
DOMAIN,
|
||||
svcName,
|
||||
svcData,
|
||||
blocking=True,
|
||||
context=intentObj.context,
|
||||
)
|
||||
|
||||
# return intent response.
|
||||
return await self.ReturnResponseByKey(intentObj, intentResponse, RESPONSE_FAVORITE_ADD_REMOVE_TRACK_OK)
|
||||
Reference in New Issue
Block a user