Last active
November 14, 2015 15:07
-
-
Save hramos/d595bfbafa24ff2b10f1 to your computer and use it in GitHub Desktop.
node-sonos-http-api client for Parse Cloud Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// controllers/alexa-skills.controller.js | |
// Based on https://github.com/mattwelch/alexa-sonos/blob/master/sonos.controller.js | |
// Adapted to use sonos-client.js | |
// Reference implementation for Alexa Skills Kit service. | |
// https://developer.amazon.com/edw/home.html#/skills | |
// alexa-nodekit node module https://github.com/brutalhonesty/alexa-nodekit | |
var alexa = require('cloud/modules/alexa-nodekit/index.js'); | |
// sonos-client.js https://gist.github.com/hramos/d595bfbafa24ff2b10f1 | |
var sonosClient = require('cloud/sonos-client.js'); | |
var _ = require("underscore"); | |
// Use whatever fits your Sonos configuration. This controller could and should be expanded to support multiple zones | |
var zone = 'Living Room'; | |
// Accept incoming Amazon Echo request. | |
// The Intent Request will be parsed for the Intent type and then forwarded to its proper function. | |
exports.index = function(req, res) { | |
var sessionId; | |
var userId; | |
if(req.body.request.type === 'LaunchRequest') { | |
alexa.launchRequest(req.body); | |
// TODO For now, we don't care about the session or the user id, we will refactor this later. | |
sessionId = alexa.sessionId; | |
userId = alexa.userId; | |
alexa.response('Welcome to Sonos. What can I play for you?', { | |
title: 'Sonos', | |
subtitle: 'Welcome to the Sonos app', | |
content: 'Some commands are "Play favorite xxx" or "Play playlist xxx"' | |
}, false, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp({message: error}); | |
} | |
return res.jsonp(response); | |
}); | |
} else if(req.body.request.type === 'IntentRequest') { | |
alexa.intentRequest(req.body); | |
// TODO For now, we don't care about the session or the user id, we will refactor this later. | |
sessionId = alexa.sessionId; | |
userId = alexa.userId; | |
var intent = alexa.intentName; | |
var slots = alexa.slots; | |
if(intent === 'PlayPlaylist') { | |
sonosClient.playlist(zone, req.body.request.intent.slots.Playlist.value).then(function(response){ | |
alexa.response('Playlist requested.', { | |
title: 'Sonos', | |
subtitle: 'Playlist requested', | |
content: 'Playlist requested.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'ListZones') { | |
sonosClient.zones().then(function(zones) { | |
console.log(zones); | |
alexa.response('Zones request received. Check your logs.', { | |
title: 'Sonos', | |
subtitle: 'Zones...', | |
content: '' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'PlayFavorite') { | |
sonosClient.favorite(zone, req.body.request.intent.slots.Favorite.value).then(function(response) { | |
alexa.response('Playing '+req.body.request.intent.slots.Favorite.value, { | |
title: 'Sonos', | |
subtitle: 'Favorite requested: '+req.body.request.intent.slots.Favorite.value, | |
content: 'Favorite requested.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'StartMusic') { | |
sonosClient.play(zone).then(function() { | |
alexa.response('Playing', { | |
title: 'Sonos', | |
subtitle: 'Music started', | |
content: 'Music started.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}, function(error) { | |
return res.status(500).jsonp(error); | |
}) | |
} else if(intent === 'StopMusic') { | |
sonosClient.pause(zone).then(function() { | |
alexa.response('Pausing', { | |
title: 'Sonos', | |
subtitle: 'Music stopped', | |
content: 'Music stopped.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}, function(error) { | |
return res.status(500).jsonp(error); | |
}) | |
} else if(intent === 'LowerVolume') { | |
sonosClient.volume(zone, "-15").then(function() { | |
alexa.response('Lowering volume', { | |
title: 'Sonos', | |
subtitle: 'Volume lowered by 15%.', | |
content: 'Volume lowered by 15%.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'RaiseVolume') { | |
sonosClient.volume(zone, "+15").then(function() { | |
alexa.response('Raising volume', { | |
title: 'Sonos', | |
subtitle: 'Volume raised by 15%.', | |
content: 'Volume raised by 15%..' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'MuteMusic') { | |
sonosClient.mute(zone).then(function() { | |
alexa.response('Muting', { | |
title: 'Sonos', | |
subtitle: 'Music muted.', | |
content: 'Music muted.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'UnmuteMusic') { | |
sonosClient.unmute(zone).then(function() { | |
alexa.response('Unmuting', { | |
title: 'Sonos', | |
subtitle: 'Unmuted', | |
content: 'Unmuted.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'PlayNext') { | |
sonosClient.next(zone).then(function() { | |
alexa.response('Playing the next track in the queue.', { | |
title: 'Sonos', | |
subtitle: 'Next track.', | |
content: 'Next track.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'PlayPrevious') { | |
sonosClient.previous(zone).then(function() { | |
alexa.response('Playing the previous track in the queue.', { | |
title: 'Sonos', | |
subtitle: 'Previous track.', | |
content: 'Previous track.' | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else if(intent === 'IdentifyMusic') { | |
sonosClient.state(zone).then(function(response) { | |
var data = response.data; | |
console.log(data); | |
var artist = data.currentTrack.artist; | |
var title = data.currentTrack.title; | |
var album = data.currentTrack.album; | |
var responseString = "Nothing is playing."; | |
var responseSubtitle = "Player may be paused or off."; | |
var responseContent = ""; | |
var playerState = data.playerState; | |
if (playerState === "PLAYING") { | |
responseString = "Currently playing " | |
responseSubtitle = "Currently playing." | |
if (artist) { | |
responseString += artist; | |
responseString += " "; | |
responseContent += artist; | |
responseContent += " "; | |
} | |
if (title) { | |
responseString += title; | |
responseString += " "; | |
responseContent += " - "; | |
responseContent += title; | |
} | |
if (album) { | |
responseContent += " - "; | |
responseContent += album; | |
} | |
} | |
alexa.response(responseString, { | |
title: 'Sonos', | |
subtitle: responseSubtitle, | |
content: responseContent | |
}, true, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
}); | |
} else { | |
alexa.response('Unknown intention, please try a different command.', { | |
title: 'Sonos', | |
subtitle: 'Unknown intention.', | |
content: 'Unknown intention, please try a different command.' | |
}, false, function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
} | |
} else { | |
alexa.sessionEndedRequest(req.body); | |
// TODO For now, we don't care about the session or the user id, we will refactor this later. | |
sessionId = alexa.sessionId; | |
userId = alexa.userId; | |
var sessionEndReason = alexa.reason; | |
alexa.response(function (error, response) { | |
if(error) { | |
return res.status(500).jsonp(error); | |
} | |
return res.jsonp(response); | |
}); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// These two lines are required to initialize Express in Cloud Code. | |
express = require('express'); | |
app = express(); | |
// Global app configuration section | |
app.set('views', 'cloud/views'); // Specify the folder to find templates | |
app.set('view engine', 'ejs'); // Set the template engine | |
app.use(express.bodyParser()); // Middleware for reading request body | |
var alexaSkillsController = require('cloud/controllers/alexa-skills.controller.js'); | |
// Invocation endpoint: https://yourapp.parseapp.com/alexaSkills | |
app.post('/alexaSkills', alexaSkillsController.index); | |
// Attach the Express app to Cloud Code. | |
app.listen(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Add these to your Amazon Skills service --> | |
{ | |
"intents": [ | |
{ | |
"intent": "ListZones", | |
"slots": [] | |
}, | |
{ | |
"intent": "PlayPlaylist", | |
"slots": [ | |
{ | |
"name": "Playlist", | |
"type": "LITERAL" | |
} | |
] | |
}, | |
{ | |
"intent": "PlayFavorite", | |
"slots": [ | |
{ | |
"name": "Favorite", | |
"type": "LITERAL" | |
} | |
] | |
}, | |
{ | |
"intent": "IdentifyMusic", | |
"slots": [] | |
}, | |
{ | |
"intent": "StartMusic", | |
"slots": [] | |
}, | |
{ | |
"intent": "StopMusic", | |
"slots": [] | |
}, | |
{ | |
"intent": "LowerVolume", | |
"slots": [] | |
}, | |
{ | |
"intent": "RaiseVolume", | |
"slots": [] | |
}, | |
{ | |
"intent": "MuteMusic", | |
"slots": [] | |
}, | |
{ | |
"intent": "UnmuteMusic", | |
"slots": [] | |
}, | |
{ | |
"intent": "PlayNext", | |
"slots": [] | |
}, | |
{ | |
"intent": "PlayPrevious", | |
"slots": [] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require('cloud/app.js'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Add these to your Amazon Skills service | |
ListZones list zones | |
StartMusic play music | |
StartMusic to play music | |
StartMusic play some music | |
StartMusic to play some music | |
StartMusic play | |
StopMusic stop | |
IdentifyMusic what's playing | |
IdentifyMusic what is playing | |
IdentifyMusic about what's playing | |
IdentifyMusic about what is playing | |
IdentifyMusic what song is this | |
IdentifyMusic which artist is this | |
IdentifyMusic who's playing | |
IdentifyMusic who is playing | |
LowerVolume lower volume | |
RaiseVolume raise volume | |
MuteMusic mute | |
UnmuteMusic unmute | |
PlayNext play next | |
PlayNext next | |
PlayPrevious play previous | |
PlayPrevious previous | |
PlayFavorite to play favorite station {favorite|Favorite} | |
PlayPlaylist start playlist {playlist|Playlist} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// node-sonos-http-api client for Parse Cloud Code | |
// Adjust these to match your node-sonos-http-api service | |
var sonosHost = "yourSonosHost.com"; | |
var sonosPort = 5005; | |
var baseUrl = "http://" + sonosHost + ":" + sonosPort | |
var _ = require("underscore"); | |
// List available zones and their status | |
exports.zones = function() { | |
var path='/zones'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
var zones = response.data; | |
prettyPrintZones(zones); | |
return Parse.Promise.as(zones); | |
}, function(error) { | |
return Parse.Promise.error('Could not play.'); | |
}); | |
}; | |
// Ask zone to start playing | |
exports.play = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/play'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Playing.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not play.'); | |
}); | |
}; | |
// Start playing playlist | |
exports.playlist = function(zone, playlist) { | |
var path = '/' + encodeURIComponent(zone) + '/playlist/' + encodeURIComponent(playlist); | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Playlist requested.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not play.'); | |
}); | |
}; | |
// Play favorite | |
exports.favorite = function(zone, favorite) { | |
var path = '/' + encodeURIComponent(zone) + '/favorite/' + encodeURIComponent(favorite); | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Favorite requested.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not play.'); | |
}); | |
}; | |
// Pause | |
exports.pause = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/pause'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Paused.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not pause.'); | |
}); | |
}; | |
// Adjust volume. Value can be absolute (0 to 100) or relative (-15 or +15 percent) | |
exports.volume = function(zone, value) { | |
var path = '/' + encodeURIComponent(zone) + '/volume/' + value; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Volume adjusted.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust volume.'); | |
}); | |
}; | |
// Same as volume, but for the entire group | |
exports.groupVolume = function(value) { | |
var path = '/groupVolume/' + value; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Group volume adjusted.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust group volume.'); | |
}); | |
}; | |
// pause all zones | |
exports.pauseAll = function(timeout) { | |
var path = '/pauseall'; | |
if (timeout) { | |
path += '/' + timeout; | |
} | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Paused all.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not pause all.'); | |
}); | |
}; | |
// resume all zones | |
exports.resumeAll = function(timeout) { | |
var path = '/resumeall'; | |
if (timeout) { | |
path += '/' + timeout; | |
} | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Resumed all.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not resume all.'); | |
}); | |
}; | |
// clear Sonos queue | |
exports.clearQueue = function() { | |
var path = '/clearqueue'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Cleared queue.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not clear queue.'); | |
}); | |
}; | |
// timeout in seconds, timestamp, or off | |
exports.sleep = function(timeout) { | |
var path = '/sleep/' + timeout; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
var status = 'Sleeping'; | |
if (timeout === 'off') { | |
status = 'Not sleeping.'; | |
} | |
return Parse.Promise.as(status); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust sleep.'); | |
}); | |
}; | |
// Skip to a track at queue index | |
exports.seek = function(zone, queueIndex) { | |
var path = '/' + encodeURIComponent(zone) + '/seek/' + queueIndex; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Seeking.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not seek.'); | |
}); | |
}; | |
// Seek forward or backwards within the current track | |
exports.trackSeek = function(zone, seconds) { | |
var path = '/' + encodeURIComponent(zone) + '/trackseek/' + seconds; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Seeking.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not seek.'); | |
}); | |
}; | |
// Mute | |
exports.mute = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/mute'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Volume muted.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not mute.'); | |
}); | |
}; | |
// Unmute | |
exports.unmute = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/unmute'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Volume unmuted.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not unmute.'); | |
}); | |
}; | |
// Repeat current track | |
exports.repeatOn = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/repeat/on'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Repeat on.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Repeat current track | |
exports.repeatOff = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/repeat/off'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Repeat off.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Shuffle queue | |
exports.shuffleOn = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/shuffle/on'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Shuffle on.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Shuffle queue | |
exports.shuffleOff = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/shuffle/off'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Shuffle off.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Crossfrade tracks | |
exports.crossfadeOn = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/crossfade/on'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Crossfade on.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Crossfade tracks | |
exports.crossfadeOff = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/crossfade/off'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Crossfade off.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not adjust.'); | |
}); | |
}; | |
// Play next track | |
exports.next = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/next'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Playing next.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not play next.'); | |
}); | |
}; | |
// Play previous track | |
exports.previous = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/previous'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Playing previous.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not play previous.'); | |
}); | |
}; | |
// Get current state | |
exports.state = function(zone) { | |
var path = '/' + encodeURIComponent(zone) + '/state'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
var data = response.data; | |
return Parse.Promise.as(response.data); | |
}, function(error) { | |
return Parse.Promise.error('Could not unmute.'); | |
}); | |
}; | |
// experimental | |
exports.lockVolumes = function() { | |
var path = '/lockvolumes'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Volumes locked.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not lock volumes.'); | |
}); | |
}; | |
exports.unlockVolumes = function() { | |
var path = '/unlockvolumes'; | |
return Parse.Cloud.httpRequest({ | |
url: baseUrl + path | |
}).then(function(response){ | |
return Parse.Promise.as('Volumes unlocked.'); | |
}, function(error) { | |
return Parse.Promise.error('Could not unlock volumes.'); | |
}); | |
}; | |
var prettyPrintZones = function(zones) { | |
var pp = ""; | |
_.each(zones, function(zone) { | |
pp += 'uuid: ' + zone.uuid + '\n'; | |
pp += 'coordinator:\n'; | |
pp += ' uuid: ' + zone.coordinator.uuid + '\n'; | |
pp += ' roomName: ' + zone.coordinator.roomName + '\n'; | |
pp += 'members: \n'; | |
_.each(zone.members, function(member) { | |
pp += ' uuid:' + member.uuid + '\n'; | |
pp += ' roomName:' + member.roomName + '\n'; | |
pp += ' playMode:\n'; | |
pp += ' ├── shuffle:' + member.playMode.shuffle + '\n'; | |
pp += ' ├── repeat:' + member.playMode.repeat + '\n'; | |
pp += ' └── crossfade:' + member.playMode.crossfade + '\n'; | |
pp += ' groupState:\n'; | |
pp += ' ├── volume:' + member.groupState.volume + '\n'; | |
pp += ' └── mute:' + member.groupState.mute + '\n'; | |
pp += ' state:\n'; | |
pp += ' ├── currentTrack:\n'; | |
pp += ' | ├── artist:' + member.state.currentTrack.artist + '\n'; | |
pp += ' | ├── title:' + member.state.currentTrack.title + '\n'; | |
pp += ' | └── album:' + member.state.currentTrack.album + '\n'; | |
pp += ' ├── nextTrack:\n'; | |
pp += ' | ├── artist:' + member.state.nextTrack.artist + '\n'; | |
pp += ' | ├── title:' + member.state.nextTrack.title + '\n'; | |
pp += ' | └── album:' + member.state.nextTrack.album + '\n'; | |
pp += ' ├── volume:' + member.state.volume + '\n'; | |
pp += ' ├── mute:' + member.state.mute + '\n'; | |
pp += ' ├── trackNo:' + member.state.trackNo + '\n'; | |
pp += ' ├── elapsedTime:' + member.state.elapsedTime + '\n'; | |
pp += ' ├── elapsedTimeFormatted:' + member.state.elapsedTimeFormatted + '\n'; | |
pp += ' ├── zoneState:' + member.state.zoneState + '\n'; | |
pp += ' ├── playerState:' + member.state.playerState + '\n'; | |
pp += ' └── zonePlayMode:\n'; | |
pp += ' ├── shuffle:' + member.state.zonePlayMode.shuffle + '\n'; | |
pp += ' ├── repeat:' + member.state.zonePlayMode.repeat + '\n'; | |
pp += ' └── crossfade:' + member.state.zonePlayMode.crossfade + '\n'; | |
}); | |
}); | |
console.log(pp); | |
}; |
Updated with sample utterances and intent schema that I am using in my SONOS Amazon Skills service.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated with Parse Cloud Code main.js and app.js, as well as a sample of the Alexa Skills controller I am using (with non-Sonos intents removed).