Sonos with node.js, my attempt!



Show first post
This topic has been closed for further comments. You can use the search bar to find a similar topic, or create a new one by clicking Create Topic at the top of the page.

397 replies

Userlevel 4
Badge +14
Yeah, I thought it was equivalent. 0 triggered the sleep fadeout instantly, I just looked at the controller and saw the sleep countdown was removed.

I added an "off" value that does it instead. Seemed more intuitive.
Userlevel 2
To set sleep to OFF the value should be an empty string "" and not "00:00:00"
so I have added the following code in module sleep.js function sleep(player, values).

code:

// zero time has to be sent as empty string
if (timestamp == "00:00:00") {
timestamp = "";
}
console.log("sleep time",timestamp)
Userlevel 2
Just send 0 as sleep time, that will disable it.

I noticed that it would reset the volumes if you set a sleep time, I assume the same is true of you send 0.


I have try with zero time without any success, it still fade out and stop playing.
Like this “http://localhost:5005/Office/sleep/0”

I'm using two grouped Play1, but that shouldn't be the problem?
Userlevel 4
Badge +14
Just send 0 as sleep time, that will disable it.

I noticed that it would reset the volumes if you set a sleep time, I assume the same is true of you send 0.
Userlevel 2
Thanks,
I will give the code a try!

I just found that you can't put the sleep function in OFF mode once it started.

I have been looking at the discovery player.js but I can't figure out how to change in the SOAP string to be able to set sleep to OFF.

SOAP.ConfigureSleepTimer.format({time: time});
.
Userlevel 4
Badge +14
Jishi you are amazing,

The sleep function works perfectly and I also love the possibility to just set it in seconds.

I discovered that the volume was fading out on the last 4-5 seconds and this could be used to take action to resume playing if you still is listening.

Could you give me a hint were to find the code to hook into to detect volume/input change?


Sorry, forgot to expand on the event thing. I was probably vague, but the resubscription only applies to the node instance. When it resubscribes, it automatically triggers an event to the subscribing device. Not other controllers.

I've never noticed the fading volume before. Since the controller actually lowers the volume, it seems to be evented, which is good. In order to act on it, you will need to listen for the "volume" event from the discovery object. It will contain a state object, that would give you the current volume, so you would need to keep a reference to the old volume on each of these emits, to have something to compare with. Something like this:

code:

var volumes = {};
discovery.on('volume', function (e) {
if (!volumes[e.uuid]) {
// We don't have a volume entry, so trhis is the first event. Just store volume
volumes[e.uuid] = e.state.volume;
return;
}

// check if the volume is lower (potential sleep triggered)
if (e.state.volume < volumes[e.uuid]) {
// Volume was lowered, do something
// to get the player, use
// discovery.getPlayerByUUID(e.uuid)
}
});
Userlevel 2
Jishi you are amazing,

The sleep function works perfectly and I also love the possibility to just set it in seconds.

I discovered that the volume was fading out on the last 4-5 seconds and this could be used to take action to resume playing if you still is listening.

Could you give me a hint were to find the code to hook into to detect volume/input change?
Userlevel 4
Badge +14

A different way could be to “always” set the timeout then the player goes from pause/standby to play, a bit like you already have implemented “function pauseAll(player, values)”, then you only have to toggle pause/play to keep listening.

Or how do I set the Sonos controller original function “Sleep timer (0:15)” from your http-api? By using this function I will be able to monitor it/change it from any Sonos controller.


That wasn't implemented, but seemed logical to add. It's added in 0.14 (requires discovery 0.14 as well), and you use it like this:

/RoomName/sleep/600 (10 minutes)
/RoomName/sleep/00:10:00

both formats are supported.
Userlevel 2
Thanks a lot Jishi,
You really spend a great amount of your time to give me a lot of new thought on this issue.

The problem seems to be the re-subscriptions of events from other controllers (if I understand you correctly).

I was using Android (Tasker) but have switched to IPhone/pad to keep my headache on a reasonable level.

A different way could be to “always” set the timeout then the player goes from pause/standby to play, a bit like you already have implemented “function pauseAll(player, values)”, then you only have to toggle pause/play to keep listening.

Or how do I set the Sonos controller original function “Sleep timer (0:15)” from your http-api? By using this function I will be able to monitor it/change it from any Sonos controller.
Userlevel 4
Badge +14
Hi Jishi,

I have been using your excellent Sonos-lirc module running on a Raspberry Pi for a long time no without any problem, but now I would like to upgrade it a bit.

At my home everyone like to start playing every Sonos player but no one ever pause it then they leave the room :D

So I would like to have this option “automatically shut off if left playing but untouched for a selectable time” to save money (using 4G broadband) and bandwidth.

I think it would be rather “simple” to have an off timer (aprox 1-2 hours) who is retrigged every time the volume or sound source is changed.

Do you have any clue how to do this?


I gave it a thought, but I can't come up with a reliable solution for your problem. First problem would be that one would probably not touch volume or the playlist for several hours. I know I don't, but maybe you are using it in a different way.

Secondly, every re-subscription of the events, will trigger an event just as an update, which will act like someone had change source, volume or whatever kind of event you would use as a keep-alive check. You could of course increase the re-subscription period, but that will probably give you other issues. Next option would be to check for changes instead, but then I think the first problem would be evident.

With a broader perspective, you could consider expanding the idea to trigger from external sources instead. The most obvious solution would be motion sensors, but that is probably too big of a project, I'm assuming. Next one would be some sort of device polling, basically associating a room, with someones mobile phone. Given the criteria that if a phone drops from your home WiFi, you would pause certain players. You could also pause everything when all phones seems to be off WiFi (aka, no one home). This could be done from the phone, or from the rPI using some sort of polling mechanism. If you had multiple access points, you could perhaps also check which one the phone is associated with (but might not work very well in a small area like a house).

Using the phones capabilities, you could also utilize some geofencing techniques, however, in a home it would probably not be as granular as you would want it to be. This would be scriptable on android devices, using different tools, iphones would probably not be as easy.
Userlevel 2
Hi Jishi,

I have been using your excellent Sonos-lirc module running on a Raspberry Pi for a long time no without any problem, but now I would like to upgrade it a bit.

At my home everyone like to start playing every Sonos player but no one ever pause it then they leave the room :D

So I would like to have this option “automatically shut off if left playing but untouched for a selectable time” to save money (using 4G broadband) and bandwidth.

I think it would be rather “simple” to have an off timer (aprox 1-2 hours) who is retrigged every time the volume or sound source is changed.

Do you have any clue how to do this?
Badge +1
Thanks you so much for your efforts, jishi! I've been quite busy, but I just saw your message, and installed the latest version. It works a lot better! Mostly everything is fine, almost like the previous version. I'll put my TTS functionality back up and see how it goes. Thanks!
Userlevel 4
Badge +14


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?


I have tried to fix the repeating issue. If you want to try it out, checkout the latest version.
Userlevel 4
Badge +14
Ok, thanks for the feedback, jishi.

The previous version of the 'say' command, was very useful to me. I had written elaborate scripts announcing weather and stuff when movement in the bathroom was detected on weekday mornings. It still is useful to announce what radio station is about to be played (I use an rf remote, and one button scrolls through a list of favourite stations). Of course, the 'say' command is experimental and a work in progress. I'll stay with current version because of the other new features. Thanks!


Well, apart from the repeating issue, it still works the same way? I take it that the regrouping isn't an issue for you?
Badge +1
Ok, thanks for the feedback, jishi.

The previous version of the 'say' command, was very useful to me. I had written elaborate scripts announcing weather and stuff when movement in the bathroom was detected on weekday mornings. It still is useful to announce what radio station is about to be played (I use an rf remote, and one button scrolls through a list of favourite stations). Of course, the 'say' command is experimental and a work in progress. I'll stay with current version because of the other new features. Thanks!
Userlevel 4
Badge +14

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?


I'm aware that issuing a "say" command to a player that has previously only issued a say command, would repeat the first say command once again. This is a bug, introduced when I tried to fix resuming. But it doesn't keep repeating it over and over again, that was what I was curious about.

The say command was contributed by another user and very experimental. The url syntax that is used isn't well equipped for this kind of stuff, and one would probably like more features for it to be useful, like:

grouping all players temporarily for a TTS message, then resume them again

Ungroup a player temporarily for a message and then regrouping it again (this works today)

Some sort of command builder with a text field where you can write anything in it and it would be escaped accordingly to allow for weird characters etc.
Badge +1
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?
Userlevel 4
Badge +14
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).
Badge +1
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?
Userlevel 4
Badge +14

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.
Badge +1
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.
Userlevel 4
Badge +14
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.
Badge +1
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!
Userlevel 4
Badge +14
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.
Badge +1
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.