Spotify Volume and playback control with Flic Twist
-
Hi,
Here's a module I programmed for controlling volume and basic Spotify playback functions with Flic Twist, in case someone else has any use for it. To use it, you'll have to register for a Spotify developer account and create a new app from their dashboard, in order to get access tokens etc.
@flichub @Emil: Is it possible to somehow get this added to the existing public Spotify "provider"? Seems a bit tedious/difficult for users
without any programming experienceto go down this particular rabbit hole.// main.js const flicapp = require('flicapp'); const http = require('http'); const datastore = require('datastore'); datastore.get('clientID', (err, key) => { const clientID = key // store this first: (datastore.put('clientID', 'YOUR CLIENT ID') datastore.get('clientSecret', (err, key) => { const clientSecret = key // store this first: (datastore.put('clientSecret', 'YOUR CLIENT SECRET') function percent(volume, decimalPlaces = 0) { const percentage = (volume * 100).toFixed(decimalPlaces); return `${percentage}`; } function spotifyStatus() { datastore.get('accessToken', (err, key) => { const accessToken = key if (key !== null) { const options = { url: 'https://api.spotify.com/v1/me/player', method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken } }; const req = http.makeRequest(options, (error, result) => { if (result.statusCode == 401) { refreshToken(); } if (result.statusCode == 204) { var status = "No active sessions." console.log(status); } if (result.statusCode == 200) { var jresp = JSON.parse(result.content); var status = jresp["is_playing"]; if (status === true) { spotifyPause(); } if (status === false) { spotifyPlay(); } } }) } }) }; function spotifyPlay() { datastore.get('accessToken', (err, key) => { const accessToken = key if (key !== null) { const options = { url: 'https://api.spotify.com/v1/me/player/play', method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken } }; const req = http.makeRequest(options, (error, result) => { if (result.statusCode == 401) { refreshToken(); } }) } }) }; function spotifyPause() { datastore.get('accessToken', (err, key) => { const accessToken = key if (key !== null) { const options = { url: 'https://api.spotify.com/v1/me/player/pause', method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken } }; const req = http.makeRequest(options, (error, result) => { if (result.statusCode == 401) { refreshToken(); } }) } }) }; function spotifyNext() { datastore.get('accessToken', (err, key) => { const accessToken = key if (key !== null) { const options = { url: 'https://api.spotify.com/v1/me/player/next', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken } }; const req = http.makeRequest(options, (error, result) => { if (result.statusCode == 401) { refreshToken(); } }) } }) }; function spotifyVolume(volume) { datastore.get('accessToken', (err, key) => { const accessToken = key if (key !== null) { const options = { url: 'https://api.spotify.com/v1/me/player/volume?volume_percent=' + volume, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + accessToken } }; const req = http.makeRequest(options, (error, result) => { if (result.statusCode == 401) { refreshToken(); } if (result.statusCode == 404) { console.log("No active sessions.") } }) } }) }; function refreshToken() { datastore.get('refreshToken', (err, key) => { const refreshToken = key if (key !== null) { const options = { url: 'https://accounts.spotify.com/api/token', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, content: "refresh_token=" + refreshToken + "&scope=user-read-playback-state%2C+user-modify-playback-state%2C+user-follow-read%2C+user-library-read%2C+streaming%2C+user-read-playback-position%2C+user-top-read%2C+user-read-currently-playing%2C+&client_id=" + clientID + "&client_secret=" + clientSecret + "&grant_type=refresh_token" }; const req = http.makeRequest(options, (error, result) => { if (!error && result.statusCode === 200) { var jresp = JSON.parse(result.content); datastore.put('accessToken', jresp["access_token"]) spotifyStatus(); } }) } }) }; flicapp.on("actionMessage", function(message) { if (message == 'playtoggle') { spotifyStatus(); } }); flicapp.on("actionMessage", function(message) { if (message == 'next') { spotifyNext(); } }); flicapp.on("virtualDeviceUpdate", function(metaData, values) { if (values.volume && metaData.virtualDeviceId == "spotify") { var volume = percent(values.volume) spotifyVolume(volume); flicapp.virtualDeviceUpdateState("Speaker", "spotify", { volume: values.volume }); } }); }); // clientID var }); // clientSecret varAs you probably can see, I'm not exactly a Javascript wizard, but it works
Enjoy.Andreas
-
@andreas-lorentsen The reason we don't have any cloud-based integrations for the Twist is that we think the latency could be too high and thus not a good customer experience. That said, how do you perceive the latency for updating the volume by rotating the Twist in this case?