Controlling a Sonos with the Griffin PowerMate
I love my Sonos, and with the still-in-beta next version of the smartphone controller app and the recently expanded functionality of the buttons on top of the players, it's even better. The folks at Sonos are working hard to reduce the friction involved in quickly getting music playing on your speakers.
Unfortunately for me, my main player is tucked inside a cabinet, driving a pair of in-wall speakers. So to do something as simple as change the volume, I've had to either dig the smartphone out, or open the cabinet door, reach behind the amp, and use the buttons on top of the player. A first-world problem, certainly, but still, it's friction.
I came up with a solution, though, that's making things much better. I have a Griffin PowerMate that is perfect for controlling the volume and current track on my Sonos player. I wired it up to a Raspberry Pi, and now controlling my tucked-away player is easier than ever. Let's take a look at how I did it.
Setup
You'll need a few things to get started:
- A Raspberry Pi (and accompanying SD card). In my case, I have the Pi running Raspbian
- A PowerMate
- A Sonos
The PowerMate will be connected to the Pi's USB port, and the Pi will receive information from both the PowerMate (through USB) and the Sonos zone (through the network). The only real work that needs to be done to get all this working is on the Pi.
Raspberry Pi Setup
Assuming you've got a fresh Raspbian install, we need to get some software installed to get things working. First we need to get node.js and npm installed. From the command line, type
wget http://nodejs.org/dist/v0.10.26/node-v0.10.26-linux-arm-pi.tar.gz
This will download the latest version (as of April, 2014) of nodejs. Too see if this is still the most up to date version, use a browser to peruse the hierarchy above, and find the url for the latest version.
Update 2/22/2015: Go ahead and stick with this old version of node. There are some nasty dependency problems with later versions that I haven't worked out yet. If you're reading this update, I haven't gotten around to finding the problem yet.
After downloading, type
tar xvf node-v0.10.26-linux-arm-pi.tar.gz
then
sudo mkdir /opt/node sudo mv node-v0.10.26-linux-arm-pi/* /opt/node
This gets node all installed under /opt/node
. Next:
sudo vi /etc/profile
(Or, instead of vi, use your favorite text editor). Add the following lines to your /etc/profile file
:
NODE_JS_HOME="/opt/node" PATH="$PATH:$NODE_JS_HOME/bin" export PATH
I'm not 100% sure it's necessary, but I like to reboot after this, and it only takes a few seconds on Pi anyway. (Instead of doing all this, you could apt-get install node, but that version is very old as of the time of this writing.)
Now that we've got node installed, we're almost ready to download the code for our Sonos to PowerMate connector, but there are two more things we need to do. One of the dependencies for the connector requires a USB development library that's not part of the stock Raspbian install. (It may be included on other RPi OSs, or on other platforms.)
We need to execute this code:
sudo apt-get install libusb-1.0-0-dev
Finally, we need to tell Raspian to make our soon-to-be-inserted PowerMate available to a group to which we are members. Type:
sudo vi /etc/udev/rules.d/95-powermate.rules
and paste the following code into the file, and save:
SUBSYSTEM=="usb", ATTRS{idVendor}=="077d", ATTRS{idProduct}=="0410", SYMLINK+="powermate", MODE="660", GROUP="input"
Now we're all set to get things really working.
Clone the repository, or download and extract the zip, and cd into the created directory. Before anything else, edit the sonospowermate.js file, and look for the string “family room” on line 165. Change this to the name of the Sonos Zone that you want to control.
Then execute the command “npm install” in the directory. This will install the node dependencies. This may take longer than you might guess, as one of the sub dependencies (node-hid) is actually being compiled on your machine. (As an aside, this step is why we needed to install the usb-dev package before.)
Once this is done, you should be all set. Plug your PowerMate into the Raspberry Pi, and type “node sonospowermate.js”, and you should see the PowerMate LED pulse for a bit while it's discovering your Sonos topology. Once the pulsing is done, it's ready for use.
Usage
Now that everything is working, you have a lot of control over your Sonos zone.
While zone is playing
- Single Press ”“ pause playing for the zone (or group the zone is in)
- Double Press ”“ next track
- Long Press ”“ previous track
- Turn Right ”“ group volume up
- Turn Left ”“ group volume down
- Push Turn Right ”“ zone player volume up (in other words, turn up only the volume of the player, leaving volume of other players in the group alone)
- Push Turn Left ”“ zone player volume down
While zone is stopped/paused
- Single Press ”“ start playing for the zone (or group the zone is in)
- Double Press ”“ play first Sonos Favorite
- Long Press ”“ play second Sonos Favorite
Turning the knob when the zone is not playing has no effect. This is entirely a design decision of necessity. My four year old was fascinated with the shiny silver knob, and would mess with it when we weren't around. We'd start up some music, and find the entire group at full, ear-splitting volume. I am delivering to you the results of some real world use testing.
Update
A new blog post here details more advanced Favorites functionality.
The Extra Mile
So now we've got everything working, and are happily controlling our Sonos with our shiny silver PowerMate. If the node script crashes though, or if your Pi reboots, your PowerMate will no longer function. So we'll use a node package called “forever” to make sure the script restarts on crash, and we'll install an init script to run sonospowermate on boot.
First, type
npm install forever -g
This will install “forever”, which monitors scripts for crashes, and starts them again if things go awry. That'll help us once we've got sonospowermate started, but we need to find a way to start things up in the first place. We'll add an init script to /etc/init.d which will take care of starting our app on system boot.
At the terminal, type
sudo vi /etc/init.d/sonospowermate
and paste the following code:
### BEGIN INIT INFO
# Provides: sonospowermate
# Required-Start:
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: SonosPowermate Node App
### END INIT INFO
export PATH=$PATH:/opt/node/bin
export NODE_PATH=$NODE_PATH:/opt/node/lib/node_modules
export HOME=/root
case "$1" in
start)
exec /opt/node/bin/forever start -p /home/pi/.forever --sourceDir /home/pi/bin/sonospowermate sonospowermate.js
;;
stop)
exec /opt/node/bin/forever stopall
;;
*)
echo "Usage: /etc/init.d/sonospowermate {start|stop}"
exit 1
;;
esac
exit 0
Note that on line 16, you may need to change the directory referenced by the ”“sourceDir option to match the directory in which your sonospowermate.js file resides.
After saving, execute the following from the command line:
sudo chmod u+x /etc/init.d/sonospowermate
Finally, execute this:
sudo update-rc.d sonospowermate defaults
Once this is all done, reboot your Pi, and after a couple sessions of pulsing LEDs, your PowerMate should be back in charge of your Sonos. If things go wrong, find me on Twitter, and we'll se if we can work through it.
Again, everything you need is available on github, so grab it and check things out. Let me know how it goes.
Next Up
Griffin is coming up with a bluetooth version of the PowerMate. I will certainly be modifying all this to use the wireless unit as soon as it comes out. This will make for a much nicer install.
I also am a bit frustrated by the very limited support for Sonos Favorites here. The user can only choose from the first two favorites, and worse, the user has to actually remember what the first two favorites are. I'm not sure of the answer, but I'm going to dig around a bit.
Also, this Leap Motion thing looks interesting”¦
Update
I've extended the Favorites functionality using PowerMates. You can read all about it here.
Comments ()