Skip to main content
Headliner II
March 5, 2013

Sonos with node.js, my attempt!

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!
    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.

    391 replies

    Contributor I
    January 22, 2014
    Hello,

    nice work. I already use your http api on my raspi.

    2 Questions:

    Is it possible to seek in a track? Go to a specific time on the current track?

    Is it possible to change the queue with a favorite but not start playing.

    What i want to do in my python program is:
    • favorite A is playing.
    • get an event (buttonpress e.g.)
    • Save the current state of the track. (where the queue is based on favorite A)
    • Change the queue with a favorite B.
    • Start playing favorite B.
    • get an event (buttonpress e.g.)
    • Switching back to favorite A. Start playing from the track and position which was saved in the first step.



    Third Question:
    Is it possible to get more information on the favorites? If it is a Radio Station or a List?

    thx
    jishiAuthor
    Headliner II
    January 22, 2014
    Hello,

    nice work. I already use your http api on my raspi.

    2 Questions:

    Is it possible to seek in a track? Go to a specific time on the current track?

    Is it possible to change the queue with a favorite but not start playing.

    What i want to do in my python program is:
    • favorite A is playing.
    • get an event (buttonpress e.g.)
    • Save the current state of the track. (where the queue is based on favorite A)
    • Change the queue with a favorite B.
    • Start playing favorite B.
    • get an event (buttonpress e.g.)
    • Switching back to favorite A. Start playing from the track and position which was saved in the first step.



    Third Question:
    Is it possible to get more information on the favorites? If it is a Radio Station or a List?

    thx


    Hi. I have implemented trackSeek in the discovery-library (mainly for the web controller), so it would be easy enough to add it to the http api as well. It takes elapsed time in seconds, but it would be possible to add some sort of parsing as well if that is needed ("0:05" instead of just 5, "1:04" instead of 64).

    Your second question, regarding switching between favorites. It would be possible, but note that you can not know which favorite that is playing (unless you store it yourself, and only control it using your python script). Are you planning on using a regular keyboard? If you are familiar with js, I have an example here: http://upload.grabbarna.se/files/keyboard.js

    That way, you wouldn't need to work through the HTTP API but instead speak directly with the sonos-component in node.js, meaning that you can run it inside the http api.

    3. I think there might be info if it is a playlist or a radio, otherwise you can work it out from the uri. There are only a few prefixes that are for radio, the once I have identified is:

    code:

    if (item.uri.startsWith("x-sonosapi-stream:") || item.uri.startsWith("x-sonosapi-radio:") || item.uri.startsWith("pndrradio:")) {
    Contributor I
    January 22, 2014
    Thanks for you answers!

    It would be nice if you can implement the trackseek in the http-api. As your api delivers both, it's not important for me if there is a parsing or not.
    I am experimenting with python and hacked a little script for the raspberry GPIOs with a button (my event which triggers the switch of the favorites) and a bunch of LEDs.
    So for this i need python (cause i didn't find a propper nodejs-api) and your http api comes right in, because making requests is very easy in python.
    My python script keeps track of which favorite is currently playing.

    For the favorites with the http-api: so i get with "favorites" all favorites titles and then with "favorite/..title..." get the uri and filter all favorites with the proper uri to get only radio-favorites? Is that correct? Or is there a simpler solution?
    But for this i have the problem that a favorite starts playing immediat.

    Crap 😉 ... no good nodejs raspberry-gpio api yet, and no good python api for sonos which supports favorites yet.
    jishiAuthor
    Headliner II
    January 22, 2014

    For the favorites with the http-api: so i get with "favorites" all favorites titles and then with "favorite/..title..." get the uri and filter all favorites with the proper uri to get only radio-favorites? Is that correct? Or is there a simpler solution?
    But for this i have the problem that a favorite starts playing immediat.


    Well, for now you will need to check the uri to make the distinction. I will see if I can extract more info about it, but it's possible that it will just say "Spotify playlist" "Itunes radio" and stuff like that...

    The reason that it starts immediately is because the http api will send play after a successful invokation, see row 217 in https://github.com/jishi/node-sonos-http-api/blob/master/lib/sonos-http-api.js:

    code:

    case "favorite":
    player.coordinator.replaceWithFavorite(options.value, function (success) {
    if (success)
    player.coordinator.play();
    });


    You could just remove that, or if you come up with a way to make it selectable or configurable. Do you want different behavior on different favorites, or different behavior permanently?
    Lyricist II
    January 22, 2014
    I have a question about using the favorites command in your sonos-ir-control script.

    I have little programming experience and none in Javascript so please forgive my simple questions. This is also the first time I've used a Raspberry Pi and the closest thing I've used to Linux was Unix and that was ~15 years ago.

    I have modified your sonos-IR-Control.js file to map to my IR commands. To execute playlist commands I use the following:

    "KEY_3": function (player) {
    player.coordinator.replaceWithFavorite("162.4 | NOAA Weather Radio (Weather)");
    player.coordinator.play();
    return true;
    },

    I have tried copying the replaceWithFavorite command from your http-api but had issues getting "success" part of the function to work.

    As a result I added the play command after the favorite command, but for certain favorites the timing is off and the play command is sent too early and missed.

    If you have the time and patience could you help me get the replacewithfavorite command into the remote-IR script?

    Thanks
    jishiAuthor
    Headliner II
    January 22, 2014


    "KEY_3": function (player) {
    player.coordinator.replaceWithFavorite("162.4 | NOAA Weather Radio (Weather)");
    player.coordinator.play();
    return true;
    },


    Hi, it takes some time to get comfortable with the asynchronous execution model that you use in this case.

    The .replaceWithFavorite(string favoriteName, function callback) takes two arguments, where the latter is a function declaration. this function will be invoked when the execution is done, and that is where you need to invoke the play command.

    So:

    code:

    "KEY_3": function (player) {
    player.coordinator.replaceWithFavorite("162.4 | NOAA Weather Radio (Weather)", function (success) {
    if (success) player.coordinator.play();
    });
    return true;
    },


    Also, the callback will get 1 argument which is true/false depending on if the invokation was successful or not.
    Lyricist II
    January 22, 2014
    Perfect! Works great.

    Previously I had tried copying the function directly out of your http-api and it threw errors when I ran the script via node. Not sure what I did but I messed up somewhere.

    This works great, Thanks!
    jishiAuthor
    Headliner II
    January 22, 2014

    It would be nice if you can implement the trackseek in the http-api.


    I pushed an update to the git repo with "trackseek" implemented. It will take total seconds that you want to seek into (for 2:00):

    /Office/trackseek/120
    Contributor I
    January 22, 2014
    I pushed an update to the git repo with "trackseek" implemented. It will take total seconds that you want to seek into (for 2:00):

    /Office/trackseek/120



    nice thx
    jishiAuthor
    Headliner II
    January 27, 2014
    Hi guys.

    I remember reading somewhere that somebody wanted the LED state of the players to be on when something was playing, and off when the player was paused/stopped.

    Realized that isn't such a hard thing to accomplish, so I did a quick function for it.

    Doing something like this:

    code:

    var SonosDiscovery = require('sonos-discovery');
    var discovery = new SonosDiscovery();
    discovery.setToggleLED(true);


    Unfortunately there is a bit of delay (1s or something). I think someone wanted it for a light sensor that would power on/off an amplifier. I thought it was a fun feature anyway, even if nobody finds it useful 🙂