Skip to content

Commit

Permalink
[ 1.0.42 ] * Added service player_set_repeat_mode that sets repeat …
Browse files Browse the repository at this point in the history
…mode for the specified Spotify Connect device.

  * Added service `player_set_shuffle_mode` that sets shuffle mode for the specified Spotify Connect device.
  * Added service `player_set_volume_level` that sets volume level for the specified Spotify Connect device.
  • Loading branch information
thlucas1 committed Jul 23, 2024
1 parent b49dc91 commit 0f3bba4
Show file tree
Hide file tree
Showing 7 changed files with 523 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Change are listed in reverse chronological order (newest to oldest).

<span class="changelog">

###### [ 1.0.42 ] - 2024/07/23

* Added service `player_set_repeat_mode` that sets repeat mode for the specified Spotify Connect device.
* Added service `player_set_shuffle_mode` that sets shuffle mode for the specified Spotify Connect device.
* Added service `player_set_volume_level` that sets volume level for the specified Spotify Connect device.

###### [ 1.0.41 ] - 2024/07/18

* Added support for Sonos device control and status updates via the Python [SoCo (Sonos Controller) API](https://docs.python-soco.com/en/latest/) package. These changes will allow you to control playback, obtain current status, and transfer Spotify Connect Player control to / from the device. Note that this works best when the device is controlled by the various Spotify Applications (e.g. Desktop, Mobile, and Web). Sonos devices that are controlled by the Sonos Applications can behave erratically when controlled by SpotifyPlus. More information on why that is can be found on the [Spotify Connect Brand Notes](https://github.com/thlucas1/homeassistantcomponent_spotifyplus/wiki/Spotify-Connect-Brand-Notes) wiki documentation page.
Expand Down
84 changes: 84 additions & 0 deletions custom_components/spotifyplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACK_FAVORITES:str = 'player_media_play_track_favorites'
SERVICE_SPOTIFY_PLAYER_MEDIA_PLAY_TRACKS:str = 'player_media_play_tracks'
SERVICE_SPOTIFY_PLAYER_RESOLVE_DEVICE_ID:str = 'player_resolve_device_id'
SERVICE_SPOTIFY_PLAYER_SET_REPEAT_MODE:str = 'player_set_repeat_mode'
SERVICE_SPOTIFY_PLAYER_SET_SHUFFLE_MODE:str = 'player_set_shuffle_mode'
SERVICE_SPOTIFY_PLAYER_SET_VOLUME_LEVEL:str = 'player_set_volume_level'
SERVICE_SPOTIFY_PLAYER_TRANSFER_PLAYBACK:str = 'player_transfer_playback'
SERVICE_SPOTIFY_PLAYLIST_CHANGE:str = 'playlist_change'
SERVICE_SPOTIFY_PLAYLIST_COVER_IMAGE_ADD:str = 'playlist_cover_image_add'
Expand Down Expand Up @@ -423,6 +426,33 @@
}
)

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,
Expand Down Expand Up @@ -810,6 +840,33 @@ async def service_handle_spotify_command(service: ServiceCall) -> None:
_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)

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.
Expand Down Expand Up @@ -1680,6 +1737,33 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:
supports_response=SupportsResponse.ONLY,
)

_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,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/spotifyplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"urllib3>=1.21.1,<1.27",
"zeroconf>=0.132.2"
],
"version": "1.0.41",
"version": "1.0.42",
"zeroconf": [ "_spotify-connect._tcp.local." ]
}
179 changes: 178 additions & 1 deletion custom_components/spotifyplus/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -3534,6 +3534,183 @@ def service_spotify_player_resolve_device_id(
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_spotify_player_set_repeat_mode(
self,
state:bool='off',
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set repeat mode for the specified Spotify Connect device.
Args:
state (str):
The repeat mode to set:
- `track` - will repeat the current track.
- `context` - will repeat the current context.
- `off` - will turn repeat off.
Default: `off`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_repeat_mode'
apiMethodParms:SIMethodParmListContext = None

try:

# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("state", state)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Repeat Mode Service", apiMethodParms)

# validations.
if deviceId is None or deviceId == "*":
deviceId = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)

# set Spotify player repeat mode.
_logsi.LogVerbose("Setting Spotify Player Repeat Mode")
self.data.spotifyClient.PlayerSetRepeatMode(state, deviceId, delay)

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise HomeAssistantError(ex.Message)
except SpotifyWebApiError as ex:
raise HomeAssistantError(ex.Message)

finally:

# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_spotify_player_set_shuffle_mode(
self,
state:bool,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set shuffle mode for the specified Spotify Connect device.
Args:
state (bool):
The shuffle mode to set:
- `True` - Shuffle user's playback.
- `False` - Do not shuffle user's playback.
Default: `False`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_shuffle_mode'
apiMethodParms:SIMethodParmListContext = None

try:

# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("state", state)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Shuffle Mode Service", apiMethodParms)

# validations.
if deviceId is None or deviceId == "*":
deviceId = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)

# set Spotify player shuffle mode.
_logsi.LogVerbose("Setting Spotify Player Shuffle Mode")
self.data.spotifyClient.PlayerSetShuffleMode(state, deviceId, delay)

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise HomeAssistantError(ex.Message)
except SpotifyWebApiError as ex:
raise HomeAssistantError(ex.Message)

finally:

# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_spotify_player_set_volume_level(
self,
volumeLevel:int,
deviceId:str=None,
delay:float=0.50,
) -> None:
"""
Set volume level for the specified Spotify Connect device.
Args:
volumeLevel (int):
The volume to set.
Must be a value from 0 to 100 inclusive.
Example: `50`
deviceId (str):
The id or name of the device this command is targeting.
If not supplied, the user's currently active device is the target.
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
Example: `Web Player (Chrome)`
delay (float):
Time delay (in seconds) to wait AFTER issuing the command to the player.
This delay will give the spotify web api time to process the change before
another command is issued.
Default is 0.50; value range is 0 - 10.
"""
apiMethodName:str = 'service_spotify_player_set_volume_level'
apiMethodParms:SIMethodParmListContext = None

try:

# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("volumeLevel", volumeLevel)
apiMethodParms.AppendKeyValue("deviceId", deviceId)
apiMethodParms.AppendKeyValue("delay", delay)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Player Set Volume Level Service", apiMethodParms)

# validations.
if deviceId is None or deviceId == "*":
deviceId = PlayerDevice.GetIdFromSelectItem(self.data.OptionDeviceDefault)

# set Spotify player volume level.
_logsi.LogVerbose("Setting Spotify Player Volume Level")
self.data.spotifyClient.PlayerSetVolume(volumeLevel, deviceId, delay)

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SpotifyApiError as ex:
raise HomeAssistantError(ex.Message)
except SpotifyWebApiError as ex:
raise HomeAssistantError(ex.Message)

finally:

# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_spotify_player_transfer_playback(self,
deviceId:str,
play:bool=True,
Expand Down Expand Up @@ -3725,7 +3902,7 @@ def service_spotify_playlist_change(self,
apiMethodParms.AppendKeyValue("public", public)
apiMethodParms.AppendKeyValue("collaborative", collaborative)
apiMethodParms.AppendKeyValue("imagePath", imagePath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Change Service", imagePath)
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Playlist Change Service", apiMethodParms)

# create Spotify playlist.
_logsi.LogVerbose("Changing Spotify Playlist Details")
Expand Down
Loading

0 comments on commit 0f3bba4

Please sign in to comment.