A Mood Ring for Sonos, Courtesy of Hue

A Mood Ring for Sonos, Courtesy of Hue

I've had a lot of fun with my Sonos system, doing all kinds of neat things to control my speakers. I've even done some things to work with the actual music output. This hack falls in to that later category.

I recently bought some Philips Hue Bloom accent lights, partly because I thought they might be useful around the house, but mostly because they looked like they might be fun to play with. (On a side note, Tickle just came out with an iPad app that lets kids learn to program while controlling Hue lights, Sphero robots and Parrot Mini-drones. This looks super neat.) I tried to think of something fun to do with my new lights, and of course my mind immediately drifted to my Sonos system. Disco lights might be fun, but maybe a bit too flashy. So I decided to have my Hue lights mirror the color palette of the currently playing song's album cover.

Now, when I fire up something off "Miracle Temple" by Mount Moriah, my two Blooms glow a deep orange and a pale green. In fact, if you have more Hue lights to devote to the cause, sonos-moodring will light them all up with colors picked out from the complete spectrum of an album cover.

Cutting to the chase

If you're not so interested in the how or why, and just want to dive in, simply clone the github repo, type npm install in the cloned directory, copy config_EXAMPLE.js to config.js and enter your LastFM and Mashape keys there. Then press the button on your Hue hub (only do this the first time), type npm start and you're off and running.

Parts/Ingredients

To get started, we'll need a few things:

  • Hue lights and bridge, of course
  • A Sonos speaker
  • A computer capable of running node.js
  • A LastFM developer account
  • A Mashape account

I'll walk you through the LastFM and Mashape setup below.

Overview

Our node app, sonos-moodring, implements the following basic flow:

  1. Sense a change in player status, either music starting, or song changing, using node-sonos-discovery
  2. Get the artist and album name of the currently playing song (and the next queued song, for caching purposes), again using node-sonos-discovery
  3. Send that artist and album information to LastFM to get a URL for the album cover art using album-cover
  4. Send the URL for the album art to the ColorTag API at Mashape
  5. Grab as many of the return colors as we have lights allocated for, and convert the hex RGB into 0-255 decimal RBG.
  6. Send that RGB info to our lights, using node-hue-api

LastFM Setup

Head to the LastFM API site and log in with your regular LastFM account (or create an account if you don't have one). Next, you'll need to create a new API Account. One the new account page, simply enter (or confirm) your email address, the name of your app, and a description. After clicking save, note the API key, as you'll need this later.

Mashape Setup

Next, head to Mashape and create a new account. Once through this process, you'll be provided a default application to work with. You can rename this application, but it's not necessary. Navigate to the ColorTag API page, scroll towards the bottom, and select your application name from the dropdown, and click "Test Endpoint". This adds the ColorTag API to your Mashape App. (I'm actually a little unclear on this whole process, as it seems like a fairly arbitrary way to add APIs. Related: I've got a bunch of APIs added to my app that I can't figure out how to delete.)

You'll have to enter credit card information here, as the ColorTag API is a for-pay product. You get 500 free call per month for free, though.

Once your app is all set, click on "Get the Keys" in the upper right of your app's page, and not the test key, needed later.

The Repo

Grab all the code from github, cd into the cloned directory, and execute npm install to get all the dependencies. It's pretty simple!

config.sys

There is a file named config_EXAMPLE.js in the root directory of sonos-moodring. Rename this file to config.js and edit it, inserting your LastFM and Mashape keys where noted.

App Settings

At the top of the index.js file is a group of variables that are used to configure the app for the end user's needs. Really only the first to variables PLAYER_NAME and LIGHT_SETTINGS HAVE to be modified. The other five variables in the top section will work just fine unmodified.

For PLAYER_NAME, simply change "family room" to the name of the Sonos zone you wish to monitor for album art. For LIGHT_SETTINGS create an array consisting of an object for each Hue light you wish to control, with the following keys and values:

{
	light: '(String)The name of your hue light. Can be found in the hue app',
	color: (integer)The color you want this light to display
}

On that color thing, the integer you use references the dominance of a color in the palette returned from ColorTag. So a color value of 1 would mean that the light would display the most dominant color. 2 would be the second most dominant, and so on. Note that there are rarely more than 5 or so colors returned, and often only 2 or 3, (or even 1, if it's a very monochromatic image).

Some Code

I primarily make use of two npm packages here, node-sonos-discovery and node-hue-api. Very pleasantly, node-hue-api is promise-based, which is great, as the the spin-up for the lights is pretty recursive:

  1. Find Hue bridges
  2. Check the status of the first one we find
  3. If we have an app on the bridge (in other words, if we've registered our app with this bridge), proceed to 5.
  4. Register our app with this bridge.
  5. Find the lights that belong to this bridge.
  6. Assign the lights we allocate to our colors, to the lights we found.

You can imagine the callback hell this might entail, but promises make it nice and tidy:

The heart of this section is on line 62. It's a lovely chain of promises, invoking functions, that themselves return promises, chaining all the way down.

The other part of our app, the Sonos side of things, uses my old favorite, node-sonos-discovery. This does not implement promises, and given the relatively small amount of work we need this library to do, and the fact that our callbacks don't need to nest I didn't spend the time to promise-ify things.

In order to save precious callouts to the ColorTag API, I used node-persist, which implements a local file-based storage for our color results. It's not at all disk-space friendly, but it works for what we need.

Wrap Up

After all that work, you should be all set. Press the button on your Hue hub (only do this the first time), type npm start, and watch your Hue lights change with your Sonos.

I've enjoyed cycling through all kinds of music in the past couple days, and seeing how their album covers affect my lights. I've found that I listen to a lot of music that features monochromatic, light album images, which doesn't make for exciting light shows. But when a really colorful album does come up, the lights look fabulous.

Next up, I think I might get one more Hue (maybe an Iris this time), and use that for my primary color, and set the two Blooms on either side, set to the secondary color.

Clone the repo and try sonos-moodring out. I'd love to see how it works for you guys!