UnknownA couple weeks ago, we had some folks were over and, as usual, we ended up listening to music on my Sonos system. Since I was the only person with a Sonos controller on my phone, I ended up fielding all the song requests. Since then, I think I've come up with a better way to get group input on a night's playlist: SMS. I considered a web app, but figured that might be too much trouble for folks to bother with. A quick SMS containing search text for a song is a super-low barrier to entry for most people.

I wrote a node app that uses Twilio to send and receive texts, and Rdio to search for and queue up songs. Take a look:

Setup

Clone this repo, and execute npm install inside the created director. This may take a while, as the sqlite3 package will possibly be compiled on your machine.

logos_downloadable_roundGet a Twilio developer account and phone number. A trial account is fine for testing, but if you're going to roll this out for a gathering, you'll want to pay the $1 for a phone number, and the $0.0075 per text, just so you don't have to add all your guests' phone number manually. Also so you don't get the “Twilio Trial” lead in all your texts. Note your auth token for later use.

After you've gotten your phone number from Twilio, you need to tell it where to send the SMS-received webhooks. (These are the calls Twilio will make to your code to notifiy you of incoming texts). Go to your numbers page and click on the number you'll be using for this project. Under the Messaging banner, enter the full url you plan to use to receive the webhooks, and select HTTP POST as the webhook type.

rdio-iconGet an Rdio developer account. This will actually end up being a mashery account. (Mashery helps companies develop and deploy APIs). Once you've got everything set up, and have created a new Rdio app, note the client id and secret.

Execution

There are four environment variables the code calls for:

1: RDIO_USER_NAME: Your Rdio username (usually your email address)

2: RDIO_KEY: Your Rdio client key

3: RDIO_SECRET: Your Rdio client secret

4: TWILIO_AUTH_TOKEN: Your Twilio Auth token

You can either set these with the traditional export method in your .bashrc (or similar), or you can just set them all at the same time before executing the sonossmsqueue script:
$ RDIO_USER_NAME=user@xxx.com RDIO_KEY=123456 RDIO_SECRET=54321 TWILIO_AUTH_TOKEN=98765 node sonossmsqueue.js

Once the script is running, you're ready to send some search texts.

Use

Text a search term to your twilio number, and wait for the result. Your search will either result in a song added to the Sonos queue, a list of songs from which to choose returned to the sender, or a “No songs found” message returned to the sender.

In the case of a list of songs returned, the user simply replies with a “1”, “2”, or “3” to select one of the three possible songs.

Code

The app uses a few node packages:

  • sonos-discovery: Used to find our player, then to add songs to its queue
  • express: used to set up the POST endpoint for our twilio webhook
  • twilio: used to send responses back to twilio
  • node-rdio: used to send queries to Rdio
  • sqlite3: used to save our list of 3 possible songs when a user's search results in more than one match

The code is pretty well documented, but there is one area that might present a bit of a challenge. On the express “app.post” method, there is a twilio.webhook middleware there that can take some optional arguments. I've left it blank in github, but in my actual installation, I've passed the URL at which my app is responding. In other words, I told Twilio to find my endpoint at http://www.myurl.com:3000. Now, my little home server, and specifically, my node app, have no idea that they are responding at that url, because they're behind a firewall, on a dyndns'ed home ip address. I could tell the app (or my server) that we're responding to that url, but it's easier in this case to pass the object “{url: 'http://www.myurl.com:3000'}” into the webhook middleware. As an aside, this information is used when the middleware constructs the hash to compare with the hash passed in the request headers to confirm that the request is in fact from Twilio.

I had (have) an ongoing problem with the webhook middleware. The x-twilio-signature header (which the middleware uses) would return different results for the exact same POST sent via the “Send SMS” developer console, causing the webhook middleware to sometimes reject the POST as not coming from Twilio. I plan to submit a ticket to technical support about this soon. In the short term, you might have to leave the twilio.webhook() line out to get things to work.

Again, the code is available on github, so clone the repo and give it a try!