openHAB integration to Spotify Web Connect API (player)

Thanks for responding. Yeah I should have mentioned this one. I was able to figure the one you just linked this morning andgot it into habpanel. But I was hoping to have more flexibility re: choosing devices and selecting certain playlists and such. And now that I have to type this out i’m questioning my decision to use something that looks like it’s not getting updates…

Ultimately, I want the ability to pick and choose devices, play certain playlists, etc in habpanel. Does this exist? if it not, where would I begin in terms of learning how to make it myself?

The Spotify Binding supports both play lists and list of devices. I guess it’s possible to either start with the widget of this topic and the one I mentioned and modify them. This can be done in happanel itself. It’s a little bit hard to find. If you have added the widget: in edit mode of a panel, in ‘add widgets’ down in the list click on the small ‘wheel’. This should show you the page of custom widgets. Clicking on the widget opens it in edit mode and then you can add playlists and select devices (devicename as a selector)

I see, that makes sense. I just went in and started playing around with those, and at those point that’s good enough for me. great actually.

Thanks for taking the time to get me going in the right direction again, I really appreciate it!

Hi Guys. Does anyone know how to make the active device name show the name of the device instead of the numeric value?

With openHAB 2.4 and the lastest version of the Spotify Binding it should just show the name (With Selection in sitemap you can make it selectable)

Hmmm. I am on openhab 2.4 and i installed the binding from the eclipse iot store. I’m trying to get this to show in habpanel and not on the sitemap. Is there something im doing wrong or is there a newer version of the binding?

On habpanel when no devices are available, but there was one previous available it seems to show the id instead of the name.

Since you are probably going to know most of your devices beforehand, I just setup individual widgets ($devicename_DevicePlay between PLAY/PAUSE) for all devices I can imagine to be using. Meanwhile in rules I use the devicename (which is id now) and it works fine.

Also… I’m trying to repurpose an old Android-phone (S4 running 5.0.1) to be a Spotify-player… Sofar I’ve been trying to get Spotify to stay alive and connected to the API with Tasker by switching the App on screen and clicking on the player tab every 2 minutes. I also use an app called Screen Standby that is discontinued for a few years now and has it’s quirks, but works! With it I can turn the screen completely black but have the screen still on with Spotify on screen. The apk can be found from various sources, and even though there was a pro-version, it is not needed. A rooted Android device is recommended too.

All this still leaves an issue where it is not possible to control the volume via Spotify API/App when playing through an Android-device… Does anyone know if casting to a Chromecast device from an Android device would suddenly allow volume control? Or am I better off looking for the cheapest Linux/Windows device that I can make in to a Spotify player? I have not setup the normal Openhab voice channels either, so those could probably made to work with a PC device too. Are there other (cheap) devices that could be used to play from Spotify with volume control?

Hello all,

i use openhab 2.4 and i’m pretty new here:).
i try to get the spotify binding running, but it seems like the spotify.items cannot be loaded:

spotify.items (freshly pulled today) is saved in the items folder, but it’s not listet in paper ui and i get an error while trying to set my client id via cli " Error: Item ‘spotify_client_id’ does not exist."
Also my log viewer shows this:
2019-01-05 12:33:12.892 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model ‘spotify.items’ has errors, therefore ignoring it: [1,1]: missing EOF at ‘<’

[885,12]: mismatched character ‘<EOF>’ expecting ‘’’

2019-01-05 12:33:20.490 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model ‘spotify.items’ has errors, therefore ignoring it: [2,1]: missing EOF at ‘<’

[886,12]: mismatched character ‘<EOF>’ expecting ‘’’

maybe you guys can help me with my issue?

You have a typo in your items file. Looks like it’s missing a " But because I can’t see your items I can’t tell for sure.

Please share the content of your spotify.items file, seem to be some errors in there, like @hilbrand also mentions.

Hi RolfV and hilbrand,

thanks for your replys.
You were right, the spotify.items was “wrong”. I Have no idea how it got there, but it was a file starting with:
<!DOCTYPE html>

<html lang=“en”>

<head>

<meta charset=“utf-8”>

<link rel=“dns-prefetch” href=“https://github.githubassets.com”>

Now i have the correct .items file. Openhab loaded it, i see the items in the paper ui and i set the client id+secret.

Thank you.
Let’s see how far i will get :slight_smile:

after i set the new auth code via http://openhabianpi:8080/static/spotify-auth.html
(client id, auth code and status successfull)
i get the response (on /usr/bin/python /etc/openhab2/scripts/spotify.py):

Successfully got state from OpenHab: spotify_client_id
Successfully got state from OpenHab: spotify_client_secret
Successfully got state from OpenHab: spotify_access_token
Successfully got state from OpenHab: spotify_refresh_token
Successfully got state from OpenHab: spotify_token_issued
Successfully got state from OpenHab: spotify_token_expiry
– Calling Token Refresh Service
{‘Content-Length’: ‘69’, ‘Keep-Alive’: ‘timeout=600’, ‘Server’: ‘nginx’, ‘Connection’: ‘keep-alive’, ‘Date’: ‘Sat, 05 Jan 2019 14:43:43 GMT’, ‘Content-Type’: ‘application/json’}
{u’error_description’: u’Invalid refresh token’, u’error’: u’invalid_grant’}
Successfully got state from OpenHab: spotify_auth_code
– Calling Token Service for the first time
{‘Content-Length’: ‘68’, ‘Keep-Alive’: ‘timeout=600’, ‘Server’: ‘nginx’, ‘Connection’: ‘keep-alive’, ‘Date’: ‘Sat, 05 Jan 2019 14:43:43 GMT’, ‘Content-Type’: ‘application/json’}
{u’error_description’: u’Invalid redirect URI’, u’error’: u’invalid_grant’}
– Calling Service: Update
{u’error’: {u’status’: 401, u’message’: u’Invalid access token’}}
_ -> Item node missing from response :(_
Successfully posted state to OpenHab: spotify_lastConnectionDateTime = 2019-01-05T14:43:44+0000
Done in 0.808665037155 seconds

whats is odd, also is that the spotify registration (http://openhabianpi:8080/static/spotify-auth.html) “forgets” my settings after refreshing the site.

do i have a problem while creating/saving the resfresh&accesstoken?

I was under the assumption you used the binding. Python script is not a binding. To use the spotify binding install the binding and not the python script. The binding is available in the eclipse market place which need to be enabled in PaperUI in Configuration system with maturity level beta and the becomes available in the list of bindings in PaperUI.

Hi All

Not sure if anyone has encountered this and knows the fix. I have a number of spotify connect devices in the home, what I find is that the wife will on her iphone choose her device with headphones and things work nicely but when I just want to use a spotify_action ‘play’ to play music it will do this on her device because I’ve not selected that I want the music to come from the stereo.

Is there a way in the rules using a ‘sendCommand’ to specify which device you want the play to occur on?

If i query the current device list:

penhab> smarthome:status spotify_device_list
[{"name": "Music Room Dot", "volume_percent": 50, "is_private_session": false, "is_active": false, "is_restricted": false, "type": "Speaker", "id": "135e7483675e72c1f1ae91eb1986250e1dbdf03b"}, {"name": "Galaxy Tab A", "volume_percent": 100, "is_private_session": false, "is_active": false, "is_restricted": false, "type": "Tablet", "id": "59843b9a5191652d680355dc2be3be0c5da92c7e"}, {"name": "Living Room Dot", "volume_percent": 50, "is_private_session": false, "is_active": false, "is_restricted": false, "type": "Speaker", "id": "7a16637316a11e964510af0e18c6e0b5be634c4a"}, {"name": "Garage Dot", "volume_percent": 50, "is_private_session": false, "is_active": false, "is_restricted": false, "type": "Speaker", "id": "d140c3a16e0bdfe15062f88de9c821cd0d111564"}, {"name": "Living Room", "volume_percent": 59, "is_private_session": false, "is_active": false, "is_restricted": false, "type": "AVR", "id": "f48564e5be42c184009e18b2d771af1a225d22c4"}]

I want ‘Living Room’ type is AVR.

If in a rule I use something like below, it won’t work :frowning:

       if (LivingRoom_Zrc_Remote.state == 6.0 && Main_Zone_Power.state == ON) {
            Zone_1_Input.sendCommand('Spotify')
            spotify_current_device_id.sendCommand("f48564e5be42c184009e18b2d771af1a225d22c4")

Any suggestions to send the audio to a specific Spotify connect device? I see it change the device id but it changes back in the log.

Regards

EDIT Solution:

}
       if (LivingRoom_Zrc_Remote.state == 6.0 && Main_Zone_Power.state == OFF) {
            Main_Zone_Power.sendCommand('ON')
            Zone_1_Input.sendCommand('Spotify')
            spotify_current_device_id.sendCommand("f48564e5be42c184009e18b2d771af1a225d22c4")
            spotify_action.sendCommand('play')
}
       if (LivingRoom_Zrc_Remote.state == 6.0 && Main_Zone_Power.state == ON) {
            Zone_1_Input.sendCommand('Spotify')
            spotify_current_device_id.sendCommand("f48564e5be42c184009e18b2d771af1a225d22c4")
            spotify_action.sendCommand('transfer_playback')
}
       if (LivingRoom_Zrc_Remote.state == 6.3) {
            Main_Zone_Power.sendCommand('OFF')
}

Hi all,

Today I have spent about an hour or two figuring out how to change the spotify_current_duration and spotify_current_progress from milliseconds to a regular mm:ss notation. I thought let me ask on the forum. I am sorry if this question has been asked before, but i couldn’t find it.

My question is: how can i change the milliseconds into a mm:ss notation?

Thanks in advance for the help!

Well. If you use the spotify binding there is a channel for it: trackDuration

Ok thanks. I’m going to take a look at the binding!

I have nighmares trying to get the sliders and stuff working properly, you would think something basic like a slider would be easy to do… I went a long route. It seemed impossible to do the mm:ss outside of properly setting u a js timer. I think it also increment the progress as time passes if hte music is playing without requiring an update from spotify or openhab. So the progress bar would move smoothly over 5 seconds and then get updated hopefully to where it was expected to be. So you don’t really need to hammer the spotify service to get smooth updates of the gui. It converts the time format into min and seconds and you can drag your music to play wherever you want on the timeline.

The code probably isn’t in great shape but works if you get oc-lazy-load working.

#widget code
           <div class="playback" oc-lazy-load="['/static/js/spotifySlider.js']" >
              <div ng-app="spotifySlider">
                <div ng-controller="MainCtrl" class="wrapper">
                  <div class="name" ng-model="duration=itemValue('spotify_current_duration')" ng-init="stoggle=true" ></div>
                  <div ng-model="slider_callbacks.options.ceil=itemValue('spotify_current_duration')"></div>
                  <div ng-if="stoggle!=false" ng-model="slider_callbacks.value=itemValue('spotify_current_progress')"></div>
                  <div ng-if="stoggle==false" ng-init="slider_callbacks.value=itemValue('spotify_current_progress')"></div>
                  <rzslider  rz-slider-model="slider_callbacks.value"
                            rz-slider-options="slider_callbacks.options"  ng-click="sendCmd('spotify_current_progress',slider_callbacks.value)"></rzslider>
                </div>
              </div>
            </div>


js file in html/js
var app = angular.module('spotifySlider', ['rzModule', 'ui.bootstrap']);

app.controller('MainCtrl', function($scope, $rootScope, $timeout) {

    $scope.slider_callbacks = {

        value: "1",
        offVal: new Date(Date.now() - Date.parse($scope.itemValue('spotify_current_update_time'))),
        stage: 8,

        options: {
            floor: 0,
            ceil: $scope.itemValue('spotify_current_duration'),
            step: 1,
            translate: function(value) {
                if (value == null || value == 0) {
                    return value;
                }
                offVal = new Date(Date.now() - Date.parse($scope.itemValue('spotify_current_update_time')));
                var modVal = new Date(value + Date.now() - Date.parse($scope.itemValue('spotify_current_update_time')));

                if ($scope.itemValue('spotify_current_playing') == 'OFF') {
                    modVal = new Date(parseInt(value));
                    //console.log('Paused value:' + value + 'and modVal' + modVal + 'for state' + $scope.itemValue('spotify_current_playing.state'));

                }
                if ($scope.stage < 8) {
                    if (parseInt($scope.duration - modVal) < 10000) {
                        $scope.stage += 1;
                    }
                }
                if ($scope.stage == 8) {
                    $scope.duration = $scope.itemValue('spotify_current_duration');
                    $scope.stage = 9;
                }
                if ($scope.stage == 9) {
                    if (modVal < 1000) {
                        $scope.stage = 1;
                    }
                }
                if ($scope.stage == 1) {
                    $scope.duration = $scope.itemValue('spotify_current_duration');
                    $scope.stage = 2;
                }

                //if (value === $scope.itemValue('spotify_current_duration')) {
                if ($scope.duration === null) {
                    $scope.duration = $scope.itemValue('spotify_current_duration');
                }
                if (value == $scope.duration) {
                    //console.log('Translate ceil value:' + value);
                    modVal = new Date(parseInt(value));
                    console.log('Translate ceil value:' + value + 'and modVal' + modVal);
                }

                if (modVal > $scope.duration) {
                    console.log('Stage: ' + $scope.stage + ' mod8 : ' + parseInt(modVal / 1000));
                    if ($scope.stage < 10) {
                        $scope.stage = 9;
                        console.log('Force update');
                        $scope.sendCmd('spotify_forceupdate', 'ON');
                    }

                    var hundreds = Math.floor($scope.stage / 100);
                    var thousands = Math.floor($scope.stage / 1000);

                    if ($scope.stage % (thousands * 1900 + hundreds * 190 + 9) == 9) {
                        console.log('Force update');
                        $scope.sendCmd('spotify_forceupdate', 'ON');
                    }
                    $scope.stage = $scope.stage + 1;
                    modVal = new Date(parseInt(modVal - $scope.duration));
                }


                //console.log('value :' + value + 'now:' + Date.now() + 'offset diff' + (Date.now() - Date.parse($scope.itemValue('spotify_current_update_time'))) + 'offVal: ' + Date.parse($scope.itemValue('spotify_current_update_time')));

                var minutes = Math.floor(modVal / 60000);
                var seconds = modVal.getSeconds(); //(value / 10000) % 60;
                var out = minutes + ":" + ("0" + seconds).slice(-2);
                //console.log('value:' + value + 'seconds' + seconds);
                //console.log('Time log value:' + out);
                return out;
            },
            onStart: function(value) {
                console.log('On Start!!!');
                $scope.stoggle = false;
                $scope.duration = $scope.itemValue('spotify_current_duration');
                $scope.offsetTime = Date.now();
                offVal = Date.now();
            },
            onChange: function(value) {
                console.log('On change!!!');
                $scope.stoggle = false;
                offVal = Date.now();
                //$scope.duration = $scope.itemValue('spotify_current_duration');
            },
            onEnd: function(value) {
                console.log('On end!!!');
                $scope.stoggle = true;
                $scope.sendCmd('spotify_current_progress', $scope.slider_callbacks.value)
                $scope.duration = $scope.itemValue('spotify_current_duration');
            },
            getTime: function() {
                $scope.offsetTime = Date.now();
                offVal = Date.now();
                return Date.now();
            }

        }
    };

Here is an image of it in action. It has start and end time in mm:ss, and you can drag that green button whereever you want.

image

NOTE: I have a heavily customised python api behind this, so you may need to change some of the functions

I just want you give you some hints for updating the
“Client ID - spotify_client_id” and “Client Secret - spotify_client_secret” via SSH Session.
For me this step was not easy and so here the noob hint:

  • Login via “openhabian” to a SSH Console Session

  • Change to openHAB CLI Console

openhab-cli console

  • Update the Client ID and Client Secret

smarthome:update spotify_client_id YOUR CLIENT ID

smarthome:update spotify_client_secret YOUR CLIENT SECRET

Thats it.
Please know also, that you have to do this again, when you restart your device. To avoid this put the two items in a persistence tool (I used InfluxDB)