Skip to content

Commit

Permalink
[ 1.0.4 ] [ 1.0.4 ] * Added service search_albums to search the Spo…
Browse files Browse the repository at this point in the history
…tify catalog for matching album criteria.

  * Added service `search_artists` to search the Spotify catalog for matching artist criteria.
  * Added service `search_audiobooks` to search the Spotify catalog for matching audiobook criteria.
  * Added service `search_episodes` to search the Spotify catalog for matching episode criteria.
  * Added service `search_shows` to search the Spotify catalog for matching show (aka podcast) criteria.
  * Added service `search_tracks` to search the Spotify catalog for matching track criteria.
  * Updated underlying `spotifywebapiPython` package requirement to version 1.0.32.
  • Loading branch information
thlucas1 committed Mar 1, 2024
1 parent edecd89 commit 4c98d7e
Show file tree
Hide file tree
Showing 9 changed files with 1,501 additions and 31 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
uses: "hacs/action@main"
with:
category: "integration"
# Remove this 'ignore' key when you have added brand images for your integration to https://github.com/home-assistant/brands
#ignore: "brands"
# Comment the following 'ignore' key when you have added brand images for your integration to https://github.com/home-assistant/brands
ignore: "brands"

validate-hassfest:
name: "HASSFest Validation"
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Change are listed in reverse chronological order (newest to oldest).

<span class="changelog">

###### [ 1.0.4 ] - 2024/03/01

* Added service `search_albums` to search the Spotify catalog for matching album criteria.
* Added service `search_artists` to search the Spotify catalog for matching artist criteria.
* Added service `search_audiobooks` to search the Spotify catalog for matching audiobook criteria.
* Added service `search_episodes` to search the Spotify catalog for matching episode criteria.
* Added service `search_shows` to search the Spotify catalog for matching show (aka podcast) criteria.
* Added service `search_tracks` to search the Spotify catalog for matching track criteria.
* Updated underlying `spotifywebapiPython` package requirement to version 1.0.32.

###### [ 1.0.3 ] - 2024/02/28

* Updated service `get_show_episodes` to include the `limit_total` argument.
Expand Down
221 changes: 214 additions & 7 deletions custom_components/spotifyplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@
SERVICE_SPOTIFY_GET_TRACK_FAVORITES:str = 'get_track_favorites'
SERVICE_SPOTIFY_GET_USERS_TOP_ARTISTS:str = 'get_users_top_artists'
SERVICE_SPOTIFY_GET_USERS_TOP_TRACKS:str = 'get_users_top_tracks'
SERVICE_SPOTIFY_SEARCH_ALBUMS:str = 'search_albums'
SERVICE_SPOTIFY_SEARCH_ARTISTS:str = 'search_artists'
SERVICE_SPOTIFY_SEARCH_AUDIOBOOKS:str = 'search_audiobooks'
SERVICE_SPOTIFY_SEARCH_EPISODES:str = 'search_episodes'
SERVICE_SPOTIFY_SEARCH_PLAYLISTS:str = 'search_playlists'
SERVICE_SPOTIFY_SEARCH_SHOWS:str = 'search_shows'
SERVICE_SPOTIFY_SEARCH_TRACKS:str = 'search_tracks'


SERVICE_SPOTIFY_GET_ALBUM_SCHEMA = vol.Schema(
Expand Down Expand Up @@ -289,6 +295,54 @@
}
)

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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
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,
Expand All @@ -298,7 +352,30 @@
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)),
vol.Optional("spotify_owned_only"): cv.boolean,
}
)

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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
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=1,max=50)),
vol.Optional("offset", default=0): vol.All(vol.Range(min=0,max=500)),
vol.Optional("market"): cv.string,
vol.Optional("include_external"): cv.string,
vol.Optional("limit_total", default=0): vol.All(vol.Range(min=0,max=9999)),
}
)

Expand Down Expand Up @@ -570,6 +647,54 @@ async def service_handle_spotify_serviceresponse(service: ServiceCall) -> Servic
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name))
response = await hass.async_add_executor_job(entity.service_spotify_get_users_top_tracks, time_range, limit, offset, 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.
Expand All @@ -579,18 +704,47 @@ async def service_handle_spotify_serviceresponse(service: ServiceCall) -> Servic
market = service.data.get("market")
include_external = service.data.get("include_external")
limit_total = service.data.get("limit_total")
spotify_owned_only = service.data.get("spotify_owned_only")
_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, spotify_owned_only)
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)

else:
raise ValueError("Unrecognized service identifier '%s' in method service_handle_spotify_serviceresponse" % service.service)

raise HomeAssistantError("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: raise # pass handled exception on through.
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.
Expand Down Expand Up @@ -642,8 +796,7 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:

# did we resolve it? if not, then log a message.
if player is None:
_logsi.LogError("Entity id value of '%s' could not be resolved to a MediaPlayerEntity instance for the '%s' method call" % (str(entity_id), service.service))
return None
raise HomeAssistantError("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))
Expand Down Expand Up @@ -831,6 +984,42 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:
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,
Expand All @@ -840,6 +1029,24 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:
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,
)

# indicate success.
_logsi.LogVerbose("Component async_setup complete")
return True
Expand Down
4 changes: 2 additions & 2 deletions custom_components/spotifyplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"issue_tracker": "https://github.com/thlucas1/homeassistantcomponent_spotifyplus/issues",
"requirements": [
"smartinspectPython==3.0.33",
"spotifywebapiPython==1.0.31",
"spotifywebapiPython==1.0.32",
"urllib3>=1.21.1,<1.27"
],
"version": "1.0.3",
"version": "1.0.4",
"zeroconf": [ "_spotify-connect._tcp.local." ]
}
Loading

0 comments on commit 4c98d7e

Please sign in to comment.