Skip to main content
INFO



Source code here: https://github.com/jishi/node-sonos-http-api

Sonos Web Controller draft (work in progress!): https://github.com/jishi/node-sonos-web-controller

Sonos IR control (requires lircd and IR reciever): https://github.com/jishi/node-sonos-remote-control



==============================================================





I know that there already is an attempt at this by this guy: http://forums.sonos.com/showthread.php?t=32643, but I found it to be a bit lacking in functionality so I decided to write up my own.



I have created a simple web-based API using what I have, which could be useful for integrating stuff with other applications. This web based API is inspired by other RESTful APIs, however I don't think it follows the correct guidelines to be called a REST API.



IT supports most basic features like:



play, pause, seek, next, prev, volume, mute, setAVTransportURI



It also supports these advanced fatures:



State of player as JSON, zone info as JSON, Play favorite item, presets (grouping, volume, avTransportURI)



You can read more in the README for each project.



To run it as a service under linux, I suggest using pm2 (https://github.com/Unitech/pm2). You need to run it in forked mode (-x)!



For Windows, you might try Winser http://jfromaniello.github.io/winser/



Cheers!
I would be ok with having two different systems than could only be controlled independently but is that possible on the same LAN?



Yes, that is no problem. Just setup the first players for the first system.



When you setup the next system, and you are using the same controller, reset the controller first (I'm talking about the official Sonos controller now). This would create a new system, on the same LAN.



The Sonos controller supports multiple households, but I'm not sure how this works if you are on the same LAN. Usually this is for supporting different systems on different networks (like, work and home). Maybe you will be able to choose if it finds two different systems, otherwise you would need to reset the controller and re-associate it when you want to switch. This is not a very common setup, that's why it might seem clunky.
Ok, i've set up two different systems on the same lan. But, as you said, the web controller attaches the the 'first response' and pulls only speakers attached to that system. Is there any way to tell the controller [your web controller] to attach to a specific ip as opposed to searching for first responding speaker/bridge?
Ok, i've set up two different systems on the same lan. But, as you said, the web controller attaches the the 'first response' and pulls only speakers attached to that system. Is there any way to tell the controller [your web controller] to attach to a specific ip as opposed to searching for first responding speaker/bridge?



Not at the moment. I'm thinking of adding a setting for household ID, just give me a few days. You will be able to find your household ID here:



http://[IP to one of the players]:1400/status/netsettings.txt



The reason for using household ID is because I know it is part of the response when searching for players, and it's the same approach that the regular controller uses.
Ok, i've set up two different systems on the same lan. But, as you said, the web controller attaches the the 'first response' and pulls only speakers attached to that system. Is there any way to tell the controller [your web controller] to attach to a specific ip as opposed to searching for first responding speaker/bridge?



I have pushed an update which supports a household filtering now. Create a file called settings.json and put the following in it:



code:

{
"household": "HHID_XXxXxxxXXXXxxXXXXx"
}




Replace it with the household ID that you can find at http://X.X.X.X:1400/status/netsettings.txt



from each system. Yours probably start with Sonos_ instead of HHID_, they changed it some time ago. Older systems will begin with HHID.
Jishi - your awesome. Works exactly like I asked. Now I can run 2+ sonos systems on the same lan using your web controller. This will be perfect for my situation as it will restrict people from only being able to listen to the music/stations/services i set whereas if i allowed them to use the SONOS controller they could do whatever they want. [the speakers will be out of reach for anyone to 'connect/sync' with their device/computer so they will be forced to use the great Jishi controller 🙂 Thanks again.
As if this controller isn't great already, should you ever get bored and find lots of time on your hands - the next feature regarding connectivity would be to only connect to speakers you set [i.e. in the settings.json file].



This is similar to the previous request, but instead of having multiple sonos systems, have one system but only be able to see/control the speakers set in the settings.json file.



I believe you touched on this in a previous post but just wanted to reiterate. The reason this scenario would be ideal would be so I could use the same music service accounts [i.e. Pandora] to control all speakers as opposed to having to purchase an account for each sonos system.



But again, your household id update is fabulous and exactly what i asked for and fits my current needs perfectly. I appreciate your work on it.



I hope to one day be able to understand how your created/scripted this controller so i can help contribute.
As if this controller isn't great already, should you ever get bored and find lots of time on your hands - the next feature regarding connectivity would be to only connect to speakers you set [i.e. in the settings.json file].



This is similar to the previous request, but instead of having multiple sonos systems, have one system but only be able to see/control the speakers set in the settings.json file.



I believe you touched on this in a previous post but just wanted to reiterate. The reason this scenario would be ideal would be so I could use the same music service accounts [i.e. Pandora] to control all speakers as opposed to having to purchase an account for each sonos system.



But again, your household id update is fabulous and exactly what i asked for and fits my current needs perfectly. I appreciate your work on it.



I hope to one day be able to understand how your created/scripted this controller so i can help contribute.




Yeah, a blacklisting/whitelisting feature wouldn't be that hard to implement, and it could even be based on room name or UUID (IP-addresses could change). Then just void all the actions that are about the go out to the players (like volume, track change etc). The one issue that might not make sense is if all players are grouped, only one of the controllers would be able to control playback (play/pause/next/prev/seek) unless you would allow anyone to control it if grouped, but then one could just group it and control it, and then remove it from the group.



However, this feature is really niche and would probably make more sense to be some sort of extension.
Hm, it seems to claim that it created the cache folder here:



/opt/node-v0.11.2-linux-arm-pi/sonos/cache



But you seem to be running the script from here:



/opt/node/sonos



Which one is correct? I haven't tested 0.11, maybe they've changed some stuff on how it discover different paths.



I assume you haven't changed the cachedir with a settings.json file?



Also, unless you have a specific reason for running 0.11, don't use it. It's unstable, and 0.11.2 is rather old. 0.11.x is not "newer" than 0.10.x, they use similar versioning as linux kernel.
never mind I was able to fix my error changing permissions. what I have run into is when running the regular web interface on a mobile device i cannot click on a button, it highlights but never actually clicks. looks like it requires a double click but on a tablet that causes a zoom not a selection if that makes sense.



Ill run it with .10 and see if that changes
what I have run into is when running the regular web interface on a mobile device i cannot click on a button, it highlights but never actually clicks. looks like it requires a double click but on a tablet that causes a zoom not a selection if that makes sense.



The regular GUI was never intended for touch devices. I have started working on an alternative view which is touch-enabled and responsive, but haven't had time to work on it lately.



If you check out the mobile branch you can access it using /m and try it out.
Thank you so much for this! Working great on my raspberry p for integration in a smart house system, and set up a cheapo RF remote to use for basic operating in the bathroom. (I.e. no tears if I drop it in the tub.)



There is one thing I can't figure out, though. How do you use the generated state.json? Do I have to use it as part of a preset.json? All I want to do is resume whatever was happening on one of my players before it was interrupted be the say command. I am trying to write a bash script that

1. Generates a state-file. (Succes)

2. Uses say for an announcement. (Succes)

3. Feeds the state.json back to resume whatever was happening before (No succes)



Any input?



(P.S. This a large thread, I've flicked through it but couldn't find anything on this subject. I'm sorry if I've overlooked it.)
Thank you so much for this! Working great on my raspberry p for integration in a smart house system, and set up a cheapo RF remote to use for basic operating in the bathroom. (I.e. no tears if I drop it in the tub.)



There is one thing I can't figure out, though. How do you use the generated state.json? Do I have to use it as part of a preset.json? All I want to do is resume whatever was happening on one of my players before it was interrupted be the say command. I am trying to write a bash script that

1. Generates a state-file. (Succes)

2. Uses say for an announcement. (Succes)

3. Feeds the state.json back to resume whatever was happening before (No succes)



Any input?



(P.S. This a large thread, I've flicked through it but couldn't find anything on this subject. I'm sorry if I've overlooked it.)




Hi, the state json was merely a feature for integration purposes, to "display" the current state of your players. It is not related to the preset function at all.



What you are talking about, "saving" the current status as a preset is another feature that hasn't been built yet (in conjunction with a preset builder). That was supposed to be part of a sandbox function for the API, but haven't had time to work on that.
I, see, jishi, thanks for letting me know, and for the great API.
Can anyone tell me how to get the 'mobile' version to work? I have everything set up and working great {the 'desktop' version}, but when i access it from my phone, its cumbersome to try and change the volume. I assume the 'mobile' version will help with this issue. But my directory structure doesn't have a '/m'. I appreciate any and all assistance.
Can anyone tell me how to get the 'mobile' version to work? I have everything set up and working great {the 'desktop' version}, but when i access it from my phone, its cumbersome to try and change the volume. I assume the 'mobile' version will help with this issue. But my directory structure doesn't have a '/m'. I appreciate any and all assistance.



You need to checkout branch "mobile"



If you used git, just invoke "git checkout mobile". If you downloaded the zip from github, you need to view the mobile branch and then the zip file will hopefully reflect the correct version.



Do note that the mobile is far from finished. You can't for instance switch zone so you are stuck with whichever it chooses on load.
Absolutely awesome library! Let's talk, I'm building a new Sonos Controller.



https://github.com/michaelmcmillan/Zone
Is there a way to determine whether a given zone is grouped or not? The output from 'state' does not seem to contain any information on this. Alternatively, is this available somewhere in the sonos service (IP:1400) pages? I can't seem to find it.



Also, the new version including pauseall/resumeall seems very useful, but I can't install it. I am obviously missing something. With this version, do I need to install sonos-discover and the api as two different node services? And then start both services? Sorry if I'm dense.
Is there a way to determine whether a given zone is grouped or not? The output from 'state' does not seem to contain any information on this. Alternatively, is this available somewhere in the sonos service (IP:1400) pages? I can't seem to find it.



Also, the new version including pauseall/resumeall seems very useful, but I can't install it. I am obviously missing something. With this version, do I need to install sonos-discover and the api as two different node services? And then start both services? Sorry if I'm dense.




Not at the moment, no. This is fairly simple to implement though. What is your use case?



If you want to upgrade, use git to get the latest version or unzip the files over the old one. Then delete the node_modules folder and run npm install again, to get the latest dependencies. Let me know it that works.
Thanks for answering!



My use case is as follows:

My home automation system (raspberry pi running domoticz and the sonos http api, communcating with a fibaro home center lite) makes a pause request to the sonos api if the motion sensor has not detected movement in the room for 15 minutes. However, I do not want the pause request to be sent if the zone is grouped, so I was trying to implement some sort of check for grouping to decide whether or not to pause. But as I said, I could not find this info anywhere in the 'state' output or anywhere on the IP:1400 pages.



I'm not at home at the moment, but I'll follow your update instructions and report back how I get on.



Thanks!
Thanks for answering!



My use case is as follows:

My home automation system (raspberry pi running domoticz and the sonos http api, communcating with a fibaro home center lite) makes a pause request to the sonos api if the motion sensor has not detected movement in the room for 15 minutes. However, I do not want the pause request to be sent if the zone is grouped, so I was trying to implement some sort of check for grouping to decide whether or not to pause. But as I said, I could not find this info anywhere in the 'state' output or anywhere on the IP:1400 pages.



I'm not at home at the moment, but I'll follow your update instructions and report back how I get on.



Thanks!




It wasn't as trivial as I first anticipated, but since the rewrite it's easier to extend the functionality with custom actions. Put this in a file called grouped.js or similar, and add it to the lib/actions folder:



code:

function checkGrouped(player, values, callback) {
var zones = player.discovery.zones;
var result = loopAndCheckIfPlayerIsGrouped(zones, player);
callback({grouped: result});
}

function loopAndCheckIfPlayerIsGrouped(zones, player) {
for (var i = 0; i < zones.length; i++) {
if (zones[i].coordinator.uuid == player.uuid && zones[i].members.length == 1) {
return false;
}
}
return true;
}

module.exports = function (api) {
api.registerAction('grouped', checkGrouped);
}




Now you can invoke:



/Office/grouped



and you will receive {"grouped":true} if the player is grouped with others. Would that work? The optimal solution would be that it would be tracked depending on the zone events, but this would be a temporary workaround until I have the time to look at it.
Thanks a lot, jishi; again!



Yes; I was being dense. I forgot all about the install procdedure, but updating/reinstalling was a breeze when I found my notes. I have had a very stable system with your previous version.



And the grouped custom action works great. Now I'll just script



code:
curl -s http://localhost:5005/{room}/grouped |grep false && curl http://localhost:5005/{room}/pause




instead of just 'pause'. I can't see any advantage to having this linked to zone actions. This is perfect.



Thank you ever so much!



PS:

I never got pm2 installed. I believe because I had trouble installing a recent enough version of node (the easiest way of updating node on raspbian was sudo npm cache clean -f ; sudo npm install -g n ; sudo n sudo n 0.8.21). Therefore, I used rc.local, and the API has always been available. I just created the following file (with execute permissions): /home/pi/node-sonos-http-api-master/rc_local.sh



I put the folloing in it:



code:
#!/bin/sh
sleep 60
cd /home/pi/node-sonos-http-api-master
/usr/local/bin/node server.js >> /home/pi/node-sonos-http-api-master/node.log 2>&1




I assume, of course that your user name is pi, and that node-sonos-http-api-master is in your home directory; change accordingly.



Then add to '/etc/rc.local' (obviouslys before the line "exit 0".)



code:
# Starting Sonos http api
sh /home/pi/node-sonos-http-api-master/rc_local.sh &




My reference for this was: http://www.raspberrypi.org/documentation/linux/usage/rc-local.md



Of course, this will not make it run as a service but good enough for me.


PS:

I never got pm2 installed. I believe because I had trouble installing a recent enough version of node (the easiest way of updating node on raspbian was sudo npm cache clean -f ; sudo npm install -g n ; sudo n sudo n 0.8.21).




There is a prebuilt binary for the pi. It is usually a few versions behind, but nevertheless newer than the one in rasbian repo:



http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-arm-pi.tar.gz



I usually use that one, and is very easy to install (it's self contained, no dependencies). The README has a oneliner for extracting it into /usr/local/ or whichever path you want to add it to.
Hi, again, jishi!



I am becoming a downright nuisance, aren't I?



Ok, so I had a total system crash, and decided to make a fresh install, and took the opprotunity to install the version of node you linked to. Very easy, yes! I installed the newest version of the sonos api, but my problem is that my 'say' scripts are behaving very wonky. The new version of 'say' seems to behave rather differently from the old one. Firstly, nothing worked because I had a sentence with an exclamation mark in the script (exclamation marks work fine from the browser addres bar). In the scripts, I use '%20' instead of spaces. This is fine; but the deal breaker now is that whenever I issue a second say command (from a script or from the browser address bar), the first say phrase is repeated, either after the new phrase or instead of it... This even survives a reboot! (I guess becaue it is in the sonos "memory".) Any idea?
Hi, again, jishi!



I am becoming a downright nuisance, aren't I?



Ok, so I had a total system crash, and decided to make a fresh install, and took the opprotunity to install the version of node you linked to. Very easy, yes! I installed the newest version of the sonos api, but my problem is that my 'say' scripts are behaving very wonky. The new version of 'say' seems to behave rather differently from the old one. Firstly, nothing worked because I had a sentence with an exclamation mark in the script (exclamation marks work fine from the browser addres bar). In the scripts, I use '%20' instead of spaces. This is fine; but the deal breaker now is that whenever I issue a second say command (from a script or from the browser address bar), the first say phrase is repeated, either after the new phrase or instead of it... This even survives a reboot! (I guess becaue it is in the sonos "memory".) Any idea?




No worries, I anticipate a certain amount of support is needed and I'm happy to help as long as people aren't to dense :)



I can not reproduce the behavior that you are seeing. What I do notice it that if the player wasn't grouped, it would repeat the same message twice, but then stop. This is of course not optimal, and started when I tried to fix the resuming of the player after a TTS command.



If the player continued to play even after a reboot, my guess is that it is your trigger script that has started repeating it self, since a player is cleared after a reboot (queue is gone, the current playing track is also gone).
I was not being clear, I meant pi reboot, not sonos reboot.



To reproduce:

Reboot the pi and the sonos player you will be using. From a browser, issue



code:

http://localhost:5005/{Room}/say/Test/en




This will produce the expected results.



Issuing



code:
http://localhost:5005/{Room}/say/This is a test sentence. This Too. And this. /en




will produce the expected result, but the phrase from the first say command (Test) is repeated after the second phrase.



Does this only happen on my system?