Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[spotify] Various enhancements and fixes #11370

Merged
merged 1 commit into from
Oct 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 51 additions & 13 deletions bundles/org.openhab.binding.spotify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,22 @@ __Common Channels:__
| playlistName | String | Read-write | The currently playing playlist. Or empty if no playing list is playing. |
| albumName | String | Read-only | Album Name of the currently playing track. |
| albumImage | RawType | Read-only | Album Image of the currently playing track. |
| albumImageUrl | String | Read-only | Url to the album Image of the currently playing track. |
| artistName | String | Read-only | Artist Name of the currently playing track. |

The `playlists` channel has 2 parameters:

| Parameter | Description |
|-----------|----------------------------------------------------------------------------|
| offset | The index of the first playlist to return. Default `0`, max `100.000` |
| limit | The maximum number of playlists to return. Default `20`, min `1`, max `50` |

The `albumImage` and `albumImageUrl` channels has 1 parameter:

| Parameter | Description |
|------------|--------------------------------------------------------------------------------------------|
| imageIndex | Index in list of to select size of the image to show. 0:large (default), 1:medium, 2:small |

Note: The `deviceName` and `playlist` channels are Selection channels.
They are dynamically populated by the binding with the user specific devices and playlists.

Expand Down Expand Up @@ -163,6 +177,19 @@ __Advanced Channels:__
| deviceActive | Switch | Read-only | Indicates if the device is active or not. Should be the same as Thing status ONLINE/OFFLINE. |
| deviceRestricted | Switch | Read-only | Indicates if this device allows to be controlled by the API or not. If restricted it cannot be controlled. |

### Actions

The bridge supports an action to play a track or other context uri.
The following actions are supported:

```
play(String context_uri)
play(String context_uri, int offset, int position_ms)
play(String context_uri, String device_id)
play(String context_uri, String device_id, int offset, int position_ms)
```


## Full Example

In this example there is a bridge configured with Thing ID __user1__ and illustrating that the bridge is authorized to play in the context of the Spotify user account __user1__.
Expand All @@ -174,24 +201,27 @@ Bridge spotify:player:user1 "Me" [clientId="<your client id>", clientSecret="<yo
Things:
device device1 "Device 1" [deviceName="<spotify device name>"]
device device2 "Device 2" [deviceName="<spotify device name>"]
Channels:
String : playlists [limit=50]
String : albumImageUrl [imageIndex=1]
}
```

spotify.items:

```
Player spotifyTrackPlayer "Player" {channel="spotify:player:user1:trackPlayer"}
String spotifyDevices "Active device [%s]" {channel="spotify:player:user1:devices"}
Switch spotifyDeviceShuffle "Shuffle mode" {channel="spotify:player:user1:deviceShuffle"}
String spotifyTrackRepeat "Repeat mode: [%s]" {channel="spotify:player:user1:trackRepeat"}
String spotifyTrackProgress "Track progress: [%s]" {channel="spotify:player:user1:trackProgress"}
String spotifyTrackDuration "Track duration: [%s]" {channel="spotify:player:user1:trackDuration"}
String spotifyTrackName "Track Name: [%s]" {channel="spotify:player:user1:trackName"}
String spotifyAlbumName "Album Name: [%s]" {channel="spotify:player:user1:albumName"}
String spotifyArtistName "Artist Name: [%s]" {channel="spotify:player:user1:artistName"}
Image spotifyAlbumImage "Album Art" {channel="spotify:player:user1:albumImage"}
String spotifyPlaylists "Playlists [%s]" {channel="spotify:player:user1:playlists"}
String spotifyPlayName "Playlist [%s]" {channel="spotify:player:user1:playlistName"}
Player spotifyTrackPlayer "Player" {channel="spotify:player:user1:trackPlayer"}
String spotifyDevices "Active device [%s]" {channel="spotify:player:user1:devices"}
Switch spotifyDeviceShuffle "Shuffle mode" {channel="spotify:player:user1:deviceShuffle"}
String spotifyTrackRepeat "Repeat mode: [%s]" {channel="spotify:player:user1:trackRepeat"}
String spotifyTrackProgress "Track progress: [%s]" {channel="spotify:player:user1:trackProgress"}
String spotifyTrackDuration "Track duration: [%s]" {channel="spotify:player:user1:trackDuration"}
String spotifyTrackName "Track Name: [%s]" {channel="spotify:player:user1:trackName"}
String spotifyAlbumName "Album Name: [%s]" {channel="spotify:player:user1:albumName"}
String spotifyArtistName "Artist Name: [%s]" {channel="spotify:player:user1:artistName"}
String spotifyAlbumImageUrl "Album Art" {channel="spotify:player:user1:albumImageUrl"}
String spotifyPlaylists "Playlists [%s]" {channel="spotify:player:user1:playlists"}
String spotifyPlayName "Playlist [%s]" {channel="spotify:player:user1:playlistName"}

String device1DeviceName {channel="spotify:device:user1:device1:deviceName"}
Player device1Player {channel="spotify:device:user1:device1:devicePlayer"}
Expand All @@ -217,7 +247,7 @@ sitemap spotify label="Spotify Sitemap" {
Text item=spotifyTrackProgress label="Track progress: [%s]"
Text item=spotifyTrackDuration label="Track duration: [%s]"
Text item=spotifyTrackName label="Track Name: [%s]"
Image item=spotifyAlbumImage label="Album Art"
Image item=spotifyAlbumImageUrl label="Album Art"
Text item=spotifyAlbumName label="Currently Played Album Name: [%s]"
Text item=spotifyArtistName label="Currently Played Artist Name: [%s]"
Selection item=spotifyPlaylists label="Playlist" icon="music"
Expand All @@ -239,6 +269,14 @@ sitemap spotify label="Spotify Sitemap" {
}
```

spotify.rules

```
val spotifyActions = getActions("spotify", "spotify:player:user1")
// play the song
spotifyActions.play("spotify:track:4cOdK2wGLETKBW3PvgPWqT")
```

## Binding model and Spotify Web API

The model of the binding is such that the bridge acts as a player in the context of a specific user.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class SpotifyBindingConstants {

public static final String CHANNEL_PLAYLISTS = "playlists";
public static final String CHANNEL_PLAYLISTNAME = "playlistName";
public static final String CHANNEL_PLAYLISTS_LIMIT = "limit";
public static final String CHANNEL_PLAYLISTS_OFFSET = "offset";

public static final String CHANNEL_PLAYED_TRACKID = "trackId";
public static final String CHANNEL_PLAYED_TRACKURI = "trackUri";
Expand All @@ -76,6 +78,8 @@ public class SpotifyBindingConstants {
public static final String CHANNEL_PLAYED_ALBUMNAME = "albumName";
public static final String CHANNEL_PLAYED_ALBUMTYPE = "albumType";
public static final String CHANNEL_PLAYED_ALBUMIMAGE = "albumImage";
public static final String CHANNEL_PLAYED_ALBUMIMAGEURL = "albumImageUrl";
public static final String CHANNEL_CONFIG_IMAGE_INDEX = "imageIndex";

public static final String CHANNEL_PLAYED_ARTISTID = "artistId";
public static final String CHANNEL_PLAYED_ARTISTURI = "artistUri";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.spotify.internal.actions;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.spotify.internal.handler.SpotifyBridgeHandler;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;

/**
* Spotify Rule Actions.
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
@ThingActionsScope(name = "spotify")
@NonNullByDefault
public class SpotifyActions implements ThingActions, ThingHandlerService {

private @Nullable ThingHandler handler;

/**
* Play a context uri (track or other) on the current active device (if null is passed for deviceID) or the given
* device at the given offset and/or position in milliseconds.
*
* @param actions Spotify Actions object.
* @param contextUri context uri (track or other)
* @param deviceId Id of the device to play on, or current device if given null
* @param offset Offset in the list, default 0.
* @param positionMs position in the track in milliseconds, default 0,
*/
@RuleAction(label = "@text/actions.play.label", description = "@text/actions.play.description")
public void play(
@ActionInput(name = "contextUri", label = "@text/actions.play.context_uri.label", description = "@text/actions.play.context_uri.description", type = "java.lang.String", required = true) String contextUri,
@ActionInput(name = "deviceId", label = "@text/actions.play.device_id.label", description = "@text/actions.play.device_id.description", type = "java.lang.String", defaultValue = "") @Nullable String deviceId,
@ActionInput(name = "offset", label = "@text/actions.play.offset.label", description = "@text/actions.play.offset.description", type = "java.lang.Integer", defaultValue = "0") final int offset,
@ActionInput(name = "positionMs", label = "@text/actions.play.positions_ms.label", description = "@text/actions.play.positions_ms.description", type = "java.lang.Integer", defaultValue = "0") final int positionMs) {
((SpotifyBridgeHandler) getThingHandler()).getSpotifyApi().playTrack(deviceId == null ? "" : deviceId,
contextUri, offset, positionMs);
}

/**
* Play a context uri (track or other) on the current active device.
*
* @param actions Spotify Actions object.
* @param contextUri context uri (track or other)
*/
public static void play(ThingActions actions, String contextUri) {
((SpotifyActions) actions).play(contextUri, null, 0, 0);
}

/**
* Play a context uri (track or other) on the current active device at the given offset and/or position in
* milliseconds.
*
* @param actions Spotify Actions object.
* @param contextUri context uri (track or other)
* @param offset Offset in the list, default 0.
* @param positionMs position in the track in milliseconds, default 0,
*/
public static void play(ThingActions actions, String contextUri, final int offset, final int positionMs) {
((SpotifyActions) actions).play(contextUri, null, positionMs, positionMs);
}

/**
* Play a context uri (track or other) on the given device.
*
* @param actions Spotify Actions object.
* @param contextUri context uri (track or other)
* @param deviceId Id of the device to play on, or current device if given null
*/
public static void play(ThingActions actions, String contextUri, @Nullable String deviceId) {
((SpotifyActions) actions).play(contextUri, deviceId, 0, 0);
}

/**
* Play a context uri (track or other) on the current active device (if null is passed for deviceID) or the given
* device at the given offset and/or position in milliseconds.
*
* @param actions Spotify Actions object.
* @param contextUri context uri (track or other)
* @param deviceId Id of the device to play on, or current device if given null
* @param offset Offset in the list, default 0.
* @param positionMs position in the track in milliseconds, default 0,
*/
public static void play(ThingActions actions, String contextUri, @Nullable String deviceId, final int offset,
final int positionMs) {
((SpotifyActions) actions).play(contextUri, deviceId, positionMs, positionMs);
}

@Override
public void setThingHandler(ThingHandler handler) {
this.handler = handler;
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}
}
Loading