/* eslint-env mocha */
/* eslint no-unused-vars: "off" */
const assert = require('assert')
const SONOS = require('../')
const Sonos = SONOS.Sonos
const nock = require('nock')
const Helpers = require('../lib/helpers')
const generateResponse = function (responseTag, serviceName, responseBody) {
const soapBody = `${(responseBody || null)}`
return Helpers.CreateSoapEnvelop(soapBody)
}
const mockRequest = function (endpoint, action, requestBody, responseTag, serviceName, responseBody) {
return nock('http://localhost:1400', { reqheaders: { soapaction: action } })
.post(endpoint, Helpers.CreateSoapEnvelop(requestBody))
.reply(200, generateResponse(responseTag, serviceName, responseBody))
}
describe('Sonos - Mock', function () {
describe('play()', function () {
it('should generate play command', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.play()
})
it('should accept a uri add => Zone Info => Select queue => seek => play', function () {
this.skip()
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp301',
'AddURIToQueueResponse',
'AVTransport',
'111'
)
mockRequest('/DeviceProperties/Control',
'"urn:schemas-upnp-org:service:DeviceProperties:1#GetZoneInfo"',
'',
'GetZoneInfoResponse',
'DeviceProperties',
'xx:xx:xx:xx:xx'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0x-rincon-queue:RINCON_xxxxxxxxxx01400#0',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Seek"',
'0TRACK_NR1',
'SeekResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.play('http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3')
})
it('should be able to accept an object instead of uri', function () {
this.skip()
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3test01',
'AddURIToQueueResponse',
'AVTransport',
'111'
)
mockRequest('/DeviceProperties/Control',
'"urn:schemas-upnp-org:service:DeviceProperties:1#GetZoneInfo"',
'',
'GetZoneInfoResponse',
'DeviceProperties',
'xx:xx:xx:xx:xx'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0x-rincon-queue:RINCON_xxxxxxxxxx01400#0',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Seek"',
'0TRACK_NR1',
'SeekResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.play({
uri: 'http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3',
metadata: 'test'
})
})
})
describe('queue()', function () {
it('should generate queue command', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp301',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.queue('http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3')
})
it('should encode html characters in uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-sonos-spotify:spotify%3atrack%3a01Bz4Mijhe7m7qRvq2Ujpn?sid=12&flags=8224&sn=201',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.queue('x-sonos-spotify:spotify%3atrack%3a01Bz4Mijhe7m7qRvq2Ujpn?sid=12&flags=8224&sn=2')
})
it('should accept object in place of uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3<test>"hello"</test>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.queue({
uri: 'http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3',
metadata: '"hello"'
})
})
it('should accept a Spotify track uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-sonos-spotify:spotify%3atrack%3a1AhDOtG9vPSOmsWgNW0BEY<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="00032020spotify%3atrack%3a1AhDOtG9vPSOmsWgNW0BEY" restricted="true"><dc:title></dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
return sonos.queue('spotify:track:1AhDOtG9vPSOmsWgNW0BEY')
})
it('should accept a Spotify EU track uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-sonos-spotify:spotify%3atrack%3a1AhDOtG9vPSOmsWgNW0BEY<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="00032020spotify%3atrack%3a1AhDOtG9vPSOmsWgNW0BEY" restricted="true"><dc:title></dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON2311_X_#Svc2311-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.setSpotifyRegion(SONOS.SpotifyRegion.EU)
return sonos.queue('spotify:track:1AhDOtG9vPSOmsWgNW0BEY')
})
it('should accept a Spotify album uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-rincon-cpcontainer:0004206cspotify%3aalbum%3a1TSZDcvlPtAnekTaItI3qO<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="0004206cspotify%3aalbum%3a1TSZDcvlPtAnekTaItI3qO" restricted="true"><dc:title></dc:title><upnp:class>object.container.album.musicAlbum</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.queue('spotify:album:1TSZDcvlPtAnekTaItI3qO')
})
it('should accept a Spotify artist top tracks uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-rincon-cpcontainer:000e206cspotify%3aartistTopTracks%3a1dfeR4HaWDbWqFHLkxsg1d<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="000e206cspotify%3aartistTopTracks%3a1dfeR4HaWDbWqFHLkxsg1d" restricted="true"><dc:title></dc:title><upnp:class>object.container.playlistContainer</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.queue('spotify:artistTopTracks:1dfeR4HaWDbWqFHLkxsg1d')
})
it('should accept a Spotify user public playlist uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-rincon-cpcontainer:10062a6cspotify%3auser%3a26iFraqozskd5POrzg68pr?sid=9&flags=10860&sn=7<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="10062a6cspotify%3auser%3a26iFraqozskd5POrzg68pr" parentID="10082664playlists" restricted="true"><dc:title>User playlist</dc:title><upnp:class>object.container.playlistContainer</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.queue('spotify:user:26iFraqozskd5POrzg68pr')
})
it('should accept a Spotify EU user public playlist uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-rincon-cpcontainer:10062a6cspotify%3auser%3a26iFraqozskd5POrzg68pr?sid=9&flags=10860&sn=7<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="10062a6cspotify%3auser%3a26iFraqozskd5POrzg68pr" parentID="10082664playlists" restricted="true"><dc:title>User playlist</dc:title><upnp:class>object.container.playlistContainer</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON2311_X_#Svc2311-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.setSpotifyRegion(SONOS.SpotifyRegion.EU)
sonos.queue('spotify:user:26iFraqozskd5POrzg68pr')
})
})
describe('playWithoutQueue()', function () {
it('should accept a Spotify artist radio uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-sonosapi-radio:spotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d?sid=12&flags=8300&sn=5<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="100c206cspotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d" parentID="10052064spotify%3aartist%3a1dfeR4HaWDbWqFHLkxsg1d" restricted="true"><dc:title>Artist Radio</dc:title><upnp:class>object.item.audioItem.audioBroadcast.#artistRadio</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.queue('spotify:artistRadio:1dfeR4HaWDbWqFHLkxsg1d')
})
it('should accept a Spotify EU artist radio uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"',
'0x-sonosapi-radio:spotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d?sid=12&flags=8300&sn=5<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="100c206cspotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d" parentID="10052064spotify%3aartist%3a1dfeR4HaWDbWqFHLkxsg1d" restricted="true"><dc:title>Artist Radio</dc:title><upnp:class>object.item.audioItem.audioBroadcast.#artistRadio</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON2311_X_#Svc2311-0-Token</desc></item></DIDL-Lite>01',
'AddURIToQueueResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.setSpotifyRegion(SONOS.SpotifyRegion.EU)
sonos.queue('spotify:artistRadio:1dfeR4HaWDbWqFHLkxsg1d')
})
})
describe('playTuneinRadio()', function () {
it('should generate play command', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0x-sonosapi-stream:34682?sid=254&flags=8224&sn=0<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="F0009202034682" parentID="L" restricted="true"><dc:title>88.5 | Jazz24 (Jazz)</dc:title><upnp:class>object.item.audioItem.audioBroadcast</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON65031_</desc></item></DIDL-Lite>',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.playTuneinRadio('34682', '88.5 | Jazz24 (Jazz)')
})
})
describe('playSpotifyRadio()', function () {
it('should generate play command', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0x-sonosapi-radio:spotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d?sid=12&flags=8300&sn=5<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="100c206cspotify%3aartistRadio%3a1dfeR4HaWDbWqFHLkxsg1d" parentID="10052064spotify%3aartist%3a1dfeR4HaWDbWqFHLkxsg1d" restricted="true"><dc:title>Queen</dc:title><upnp:class>object.item.audioItem.audioBroadcast.#artistRadio</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON3079_X_#Svc3079-0-Token</desc></item></DIDL-Lite>',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.playSpotifyRadio('1dfeR4HaWDbWqFHLkxsg1d', 'Queen')
})
})
describe('setAVTransportURI()', function () {
it('should generate queue command', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.setAVTransportURI('http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3')
})
it('should accept object in place of uri', function () {
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"',
'0http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3<test>"hello"</test>',
'SetAVTransportURIResponse',
'AVTransport'
)
mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#Play"',
'01',
'PlayResponse',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
sonos.setAVTransportURI({
uri: 'http://livingears.com/music/SceneNotHeard/091909/Do You Mind Kyla.mp3',
metadata: '"hello"'
})
})
})
describe('createPlaylist()', function () {
it('should create new playlist', async function () {
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#CreateSavedQueue"',
'0Test',
'CreateSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.createPlaylist('Test')
scope.done()
})
})
describe('deletePlaylist()', function () {
it('should remove existing playlist', async function () {
const scope = mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#DestroyObject"',
'SQ:5',
'DestroyObject',
'ContentDirectory'
)
const sonos = new Sonos('localhost', 1400)
await sonos.deletePlaylist(5)
scope.done()
})
})
describe('addToPlaylist()', function () {
it('should add track to playlist', async function () {
mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"',
'BrowseDirectChildren*0100SQ:1',
'BrowseResponse',
'ContentDirectory',
'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="x-file-cifs://localhost/Music/Song.mp4" parentID="SQ:1" restricted="true"><res protocolInfo="x-file-cifs:*:audio/mp4:*">x-file-cifs://localhost/Music/Song.mp3</res><upnp:albumArtURI></upnp:albumArtURI><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>MyInterpret</dc:creator><upnp:album>MyAlbum</upnp:album><upnp:originalTrackNumber>1</upnp:originalTrackNumber></item></DIDL-Lite>000'
)
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToSavedQueue"',
'0SQ:10x-file-cifs://localhost/Music/Song.mp3<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="S://localhost/Music/Song.mp3" parentID="A:TRACKS" restricted="true"><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><upnp:albumArtURI></upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">RINCON_AssociatedZPUDN</desc></item></DIDL-Lite>4294967295',
'AddURIToSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.addToPlaylist(1, 'x-file-cifs://localhost/Music/Song.mp3')
scope.done()
})
it('should add album to playlist', async function () {
mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"',
'BrowseDirectChildren*0100SQ:1',
'BrowseResponse',
'ContentDirectory',
'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="x-file-cifs://localhost/Music/Song.mp4" parentID="SQ:1" restricted="true"><res protocolInfo="x-file-cifs:*:audio/mp4:*">x-file-cifs://localhost/Music/Song.mp3</res><upnp:albumArtURI></upnp:albumArtURI><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>MyInterpret</dc:creator><upnp:album>MyAlbum</upnp:album><upnp:originalTrackNumber>1</upnp:originalTrackNumber></item></DIDL-Lite>000'
)
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToSavedQueue"',
'0SQ:10x-rincon-playlist://localhost/Music/Album#A:ALBUMS/MyAlbum<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="A:ALBUMS/MyAlbum" parentID="A:ALBUMS" restricted="true"><dc:title>MyAlbum</dc:title><upnp:class>object.item.audioItem.musicAlbum</upnp:class><upnp:albumArtURI></upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">RINCON_AssociatedZPUDN</desc></item></DIDL-Lite>4294967295',
'AddURIToSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.addToPlaylist(1, 'x-rincon-playlist://localhost/Music/Album#A:ALBUMS/MyAlbum')
scope.done()
})
it('should add albumartist to playlist', async function () {
mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"',
'BrowseDirectChildren*0100SQ:1',
'BrowseResponse',
'ContentDirectory',
'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="x-file-cifs://localhost/Music/Song.mp4" parentID="SQ:1" restricted="true"><res protocolInfo="x-file-cifs:*:audio/mp4:*">x-file-cifs://localhost/Music/Song.mp3</res><upnp:albumArtURI></upnp:albumArtURI><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>MyInterpret</dc:creator><upnp:album>MyAlbum</upnp:album><upnp:originalTrackNumber>1</upnp:originalTrackNumber></item></DIDL-Lite>000'
)
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToSavedQueue"',
'0SQ:10x-rincon-playlist://localhost/Music/AlbumArtist#A:ALBUMARTIST/My Album Artist<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="A:ALBUMARTIST/My%20Album%20Artist" parentID="A:ALBUMARTIST" restricted="true"><dc:title>My Album Artist</dc:title><upnp:class>object.item.audioItem.musicArtist</upnp:class><upnp:albumArtURI></upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">RINCON_AssociatedZPUDN</desc></item></DIDL-Lite>4294967295',
'AddURIToSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.addToPlaylist(1, 'x-rincon-playlist://localhost/Music/AlbumArtist#A:ALBUMARTIST/My Album Artist')
scope.done()
})
it('should add genre to playlist', async function () {
mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"',
'BrowseDirectChildren*0100SQ:1',
'BrowseResponse',
'ContentDirectory',
'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="x-file-cifs://localhost/Music/Song.mp4" parentID="SQ:1" restricted="true"><res protocolInfo="x-file-cifs:*:audio/mp4:*">x-file-cifs://localhost/Music/Song.mp3</res><upnp:albumArtURI></upnp:albumArtURI><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>MyInterpret</dc:creator><upnp:album>MyAlbum</upnp:album><upnp:originalTrackNumber>1</upnp:originalTrackNumber></item></DIDL-Lite>000'
)
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#AddURIToSavedQueue"',
'0SQ:10x-rincon-playlist://localhost/Music/Genre#A:GENRE/MyGenre<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="A:GENRE/MyGenre" parentID="A:GENRE" restricted="true"><dc:title>MyGenre</dc:title><upnp:class>object.container.genre.musicGenre</upnp:class><upnp:albumArtURI></upnp:albumArtURI><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">RINCON_AssociatedZPUDN</desc></item></DIDL-Lite>4294967295',
'AddURIToSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.addToPlaylist(1, 'x-rincon-playlist://localhost/Music/Genre#A:GENRE/MyGenre')
scope.done()
})
})
describe('removeFromPlaylist()', function () {
it('should remove track from playlist', async function () {
mockRequest('/MediaServer/ContentDirectory/Control',
'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"',
'BrowseDirectChildren*0100SQ:1',
'BrowseResponse',
'ContentDirectory',
'<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="x-file-cifs://localhost/Music/Song.mp4" parentID="SQ:1" restricted="true"><res protocolInfo="x-file-cifs:*:audio/mp4:*">x-file-cifs://localhost/Music/Song.mp3</res><upnp:albumArtURI></upnp:albumArtURI><dc:title>Song</dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:creator>MyInterpret</dc:creator><upnp:album>MyAlbum</upnp:album><upnp:originalTrackNumber>1</upnp:originalTrackNumber></item></DIDL-Lite>000'
)
const scope = mockRequest('/MediaRenderer/AVTransport/Control',
'"urn:schemas-upnp-org:service:AVTransport:1#ReorderTracksInSavedQueue"',
'0SQ:102',
'ReorderTracksInSavedQueue',
'AVTransport'
)
const sonos = new Sonos('localhost', 1400)
await sonos.removeFromPlaylist(1, 2)
scope.done()
})
})
})
describe('DeviceDiscovery', function () {
it('should emit a timeout event when timeout is hit', function (done) {
const failTimeout = setTimeout(function () {
assert(false, 'Event never fired')
done()
}, 100)
const search = SONOS.DeviceDiscovery({ timeout: 10 }, function (device, model) {})
search.on('timeout', function () {
clearTimeout(failTimeout)
assert(true)
done()
})
})
it('should not emit a timeout event when no timeout option is passed in', function (done) {
setTimeout(function () {
assert(true)
done()
}, 10)
const search = SONOS.DeviceDiscovery(function (device, model) {})
search.on('timeout', function () {
assert(false, 'Timeout event should never fire')
done()
})
})
it('should not emit a timeout event after search is stopped', function (done) {
const search = SONOS.DeviceDiscovery({ timeout: 10 }, function (device, model) {})
search.on('timeout', function () {
assert(false, 'Timeout event should never fire')
done()
})
search.destroy(function () {
assert(true)
done()
})
})
})
describe('Async DeviceDiscovery', function () {
it('should reject after timeout without devices found', function (done) {
const failTimeout = setTimeout(function () {
assert(false, 'Event never fired')
done()
}, 100)
const deviceDiscovery = new SONOS.AsyncDeviceDiscovery()
deviceDiscovery.discover({ timeout: 10 }).then((device, model) => {
// Also fine if we found a device....
clearTimeout(failTimeout)
assert(true)
done()
}).catch(err => { // eslint-disable-line handle-callback-err
assert(err instanceof Error)
clearTimeout(failTimeout)
assert(true)
done()
})
})
})
describe('Sonos - Device', function () {
let sonos
before(function () {
if (!process.env.SONOS_HOST) {
this.skip()
} else {
sonos = new Sonos(process.env.SONOS_HOST, 1400)
}
})
it('should getMuted()', function () {
return sonos.getMuted().then(muted => {
assert(typeof muted === 'boolean', 'muted is a boolean')
})
})
it('should getCurrentState()', function () {
return sonos.getCurrentState().then(state => {
assert(typeof state === 'string', 'state is a string')
const values = ['stopped', 'playing', 'paused', 'transitioning', 'no_media']
assert(values.indexOf(state) > -1, 'state is one of the allowed values')
})
})
it('should getVolume()', function () {
return sonos.getVolume().then(volume => {
assert(typeof volume === 'number', 'volume is a number')
assert((volume >= 0 && volume <= 100), 'volume is between 0 and 100')
})
})
it('should getPlayMode()', function () {
return sonos.getPlayMode().then(playmode => {
assert(typeof playmode === 'string', 'playmode is a string')
const values = ['NORMAL', 'REPEAT_ONE', 'REPEAT_ALL', 'SHUFFLE', 'SHUFFLE_NOREPEAT', 'SHUFFLE_REPEAT_ONE']
assert(values.indexOf(playmode) > -1, 'playmode is one of the allowed values')
})
})
it('should getFavorites()', function () {
return sonos.getFavorites().then(function (favs) {
assert(favs.items, 'should have items')
})
})
it('should getMusicLibrary("sonos_playlists")', function () {
return sonos.getMusicLibrary('sonos_playlists').then(function (playlists) {
assert(playlists.items, 'should have items')
if (playlists.items.length > 0) {
assert('id' in playlists.items[0], 'item should have id in object')
assert('parentID' in playlists.items[0], 'item should have parentID in object')
assert('uri' in playlists.items[0], 'item should have uri in object')
assert('title' in playlists.items[0], 'item should have title in object')
}
})
})
it('should getPlaylist()', function () {
return sonos.getPlaylist('1').then(function (playlist) {
assert(playlist.items, 'should have items')
})
})
it('should getQueue()', function () {
return sonos.getQueue().then(function (queue) {
assert(queue.items, 'should have items')
})
})
// There seem to be some kind of error here.... Needs attention.
it('should getFavoritesRadioStations()', function () {
return sonos.getFavoritesRadioStations().then(function (radio) {
assert(radio.items, 'should have items')
})
})
it('should getAllGroups()', function () {
return sonos.getAllGroups().then(function (groups) {
assert(Array.isArray(groups), 'should return an array')
})
})
it('playNotification()', function () {
return sonos.playNotification({
uri: 'https://www.zapsplat.com/wp-content/uploads/2015/sound-effects-the-sound-pack-tree/tspt_pull_bell_02_065.mp3?_=1',
volume: 10
}).then(result => {
assert(result, 'Played notification')
})
})
it('playNotification() -> No notification when not playing', async function () {
const state = await sonos.getCurrentState()
if ((state === 'playing' || state === 'transitioning')) {
this.skip('Is playing cannot test')
}
return sonos.playNotification({
uri: 'https://www.zapsplat.com/wp-content/uploads/2015/sound-effects-the-sound-pack-tree/tspt_pull_bell_02_065.mp3?_=1',
volume: 10,
onlyWhenPlaying: true
}).then(result => {
assert(result === false, 'Shouldn\'t start when not playing')
})
})
it('should return Spotify Conenct Info', function () {
return sonos.getSpotifyConnectInfo()
.then(data => {
assert(true)
})
})
describe('AlarmClockService()', function () {
it('should list alarms', function () {
return sonos.alarmClockService()
.ListAlarms().then(function (result) {
assert(Array.isArray(result.Alarms), '.Alarms should be an array')
assert(typeof (result.CurrentAlarmListVersion) === 'string', '.CurrentAlarmListVersion should be a string')
})
})
it('should throw an error on update with not string', function () {
return sonos.alarmClockService().SetAlarm(1, false).catch(err => {
assert(err, 'Got error')
})
})
it('should toggle the first alarm', function () {
return sonos.alarmClockService()
.ListAlarms().then(function (result) {
if (result.Alarms.length === 0) {
assert(true)
} else {
const alarm = result.Alarms[0]
const toggleTo = alarm.Enabled === '0'
return sonos.alarmClockService().SetAlarm(alarm.ID, toggleTo).then(function (result) {
// Revert back the change, or you'll get a bad morning after testing this library....
return sonos.alarmClockService().SetAlarm(alarm.ID, !toggleTo).then(function (result) {
assert(true)
})
})
}
})
})
})
})