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

Allow speaker with no associated playlist ID #19

Merged
merged 1 commit into from
Apr 26, 2022
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
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ This is Homekit's behaviour to set back the brightness to 100% when a device is
To use this plugin you must provide some authentication information to Spotify and those steps has to be done manually.

1. Create a Spotify application in the developer's dashboard
To do so, go to https://developer.spotify.com/dashboard and create an app. Once this is done you will have your clientId and clientSecret.

To do so, go to [Spotify Dev Dashboard](https://developer.spotify.com/dashboard) and create an app. Once this is done you will have your clientId and clientSecret.

2. Obtain the auth code that this plugin will need to generate the access and refresh tokens

To do so, you need to allow access to the app you created at the previous step. You only need to do this once.

```
```md
https://accounts.spotify.com/authorize?client_id={clientId}&response_type=code&redirect_uri=https://example.com/callback&scope={scopes}
```

Expand All @@ -57,11 +57,12 @@ To use this plugin you must provide some authentication information to Spotify a
- You will have a small agreement form, simply accept it.
- Then you will be redirected and you will find your code in the URL

```
```md
Example, you will get an URL that looks like the following. The code is everything that follows `code=`.

https://example.com/callback?code=AQDPqT0ctdUm-uE2JRwbAoaWA-iRm0OuGY7wI17zQUlTxw7JfRma6id1mq-m8xKH6vJVNutJSqQcBrPZ__81uF-hrSJ-q_AX2yUEwERQKTnaPLDFCIE-c_qBjg81JSd5FqmEpJ5j9ddgKvkWUJ6WK5Kj-npTypCrUoQWRn9Vkn33DlYOfU7BxgPAPQBXQtqIfub3S576-gdUOGUAGPd6Ud5esSNMeI2lFKb-sj4eMiQJJJb35VI__EkRuFFJNCZkFagr3rBI-GGzfQA
```
```

3. Take the code obtained at step #2 and put it in your homebridge `config.json` as the value of the attribute `spotifyAuthCode`. Once that is done, restart Homebridge and you should be up and running. Look at the logs for ay errors.

For more details, see the [official auth documentation](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow)
Expand Down Expand Up @@ -95,9 +96,10 @@ If you run into issues or you need help please use the [issues template](https:/
If you haven't done it already, start by reading [this section](#distinction-between-spotify-connect-and-spotify-connect-api).

Common issues related to that though could be:
- The Homebridge instance on which this plugin is installed is not on the same network as the Spotify device
- The Spotify device is currently tied to another user account. Example, you authenticated this plugin using your account, and the Spotify device was last played with your SO's account.
- The device is in sleep mode. Any devices that sleeps (e.g. a computer) won't be available via the API.

- The Homebridge instance on which this plugin is installed is not on the same network as the Spotify device
- The Spotify device is currently tied to another user account. Example, you authenticated this plugin using your account, and the Spotify device was last played with your SO's account.
- The device is in sleep mode. Any devices that sleeps (e.g. a computer) won't be available via the API.

## Contributors

Expand Down
3 changes: 1 addition & 2 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
"spotifyPlaylistUrl": {
"title": "Spotify Playlist URL",
"description": "The URL of the Spotify playlist that you want to play when this device will be set to ON in Homekit.",
"type": "string",
"required": true
"type": "string"
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"displayName": "Homebridge Spotify Speaker",
"name": "@poblouin/homebridge-spotify-speaker",
"version": "1.1.1",
"version": "1.2.0",
"description": "Homebridge plugin that creates a speaker that plays a specific Spotify playlist",
"license": "MIT",
"author": "Pierre-Olivier Blouin <poblouin@pm.me>",
Expand Down
6 changes: 6 additions & 0 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ export class HomebridgeSpotifySpeakerPlatform implements DynamicPlatformPlugin {

private extractPlaylistId(playlistUrl: string): string | null {
try {
// Empty playlist ID is allowed for cases where one wants to only
// play or pause one speaker started from an external source.
if (!playlistUrl) {
return null;
}

const url = new URL(playlistUrl);
const playlistId = url.pathname.split('/')[2];
this.log.debug(`Found playlistId: ${playlistId}`);
Expand Down
37 changes: 32 additions & 5 deletions src/spotify-speaker-accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,46 @@ export class SpotifySpeakerAccessory {

private async setCurrentStates() {
const state = await this.platform.spotifyApiWrapper.getPlaybackState();
const playingHref = state?.body?.context?.href;
const playingDeviceId = state?.body?.device?.id;

// Make sure that this accessory is the one playing i.e. the playlist ID
// playing is the one from this accessory.
if (!state?.body?.context?.href?.includes(this.accessory.context.playlistId)) {
if (!state || state.statusCode !== 200) {
this.activeState = false;
this.currentVolume = 0;
return;
}

if (state?.statusCode === 200) {
if (state.body.is_playing && this.isPlaying(playingHref, playingDeviceId)) {
this.activeState = state.body.is_playing;
this.currentVolume = state.body.device.volume_percent;
} else if (!state || state.statusCode === 204) {
} else {
this.activeState = false;
this.currentVolume = 0;
}
}

/**
* Finds which speaker should be synced.
*
* Speakers tied to a playlist will have priority over lone
* speakers (no playlist ID associated).
*
* @param playingHref The href of the currently playing Spotify playlist
* @param playingDeviceId The spotify device ID
*/
private isPlaying(playingHref: string | undefined, playingDeviceId: string | undefined) {
const contextPlaylistId = this.accessory.context.playlistId;

if (contextPlaylistId && playingHref?.includes(contextPlaylistId)) {
return true;
}

const currentDevicePlaying = this.device.spotifyDeviceId === playingDeviceId;
const hasHigherPrioritySpeaker = this.platform.accessories.find((a) => playingHref?.includes(a.context.playlistId));
if (currentDevicePlaying && !contextPlaylistId && !hasHigherPrioritySpeaker) {
return true;
}

return false;
}
}