Wednesday, July 27, 2016

Sonos Control with the Amazon Echo - How I did it

I've had my Amazon Echo for a year or so and one thing that I've wanted ever since I purchased it, is to be able to use voice commands to control my Sonos speakers.  I waited patiently (OK  not patiently) for Sonos skill to appear on the Echo but it still hasn't come.  I was encouraged when Sonos announced it was going to focus more on voice but still nothing.  Isn't there any way to control my Sonos with my Echo?

Yes - sort of.

WARNING: This post is not for the faint of heart because the solution is not trivial.

So, I found a project on github that uses another project on github combined with a custom skill on
the Echo via a web service hosted by AWS Lambda.  OK, that was the easy part.  I knew that going in.  That's why, initially, I waited.  There must be an elegant solution out there.  I searched and waited and searched again and waited again.  Finally I decided to give it a try.  If it worked it would be really cool.  If it didn't, well I'm no worse off than before.

Now that it was decided, I sat down and went through the instructions provided by Ryan Graciano.  Here are the instructions with my extra notes.

Get jishi's node-sonos-http-api working

  1. Install node.js on a server on the same network as your Sonos.
    • Oddly enough, I didn't see this step which may have been a good thing because I may have done it wrong.  
  2. Grab https://github.com/jishi/node-sonos-http-api and run it on that server. On Mac, it's "npm install https://github.com/jishi/node-sonos-http-api", then go to the directory created and "npm start".
    • So I tried the command and, not unexpectedly, it failed miserably.  OK now I have to find out what "npm" is and how to install it.  Here's what I did.
      • I found the npm website and created an account (not sure if I really needed to do that)
      • Downloaded the program from https://nodejs.org/download/release/latest/ 
        • This was hard to find because there are a lot of options and it took a bit to find the right one (it was the node-v6.3.1-x86.msi for my 32 bit Windows machine) 
      • Installed the program 
    • Then I had to grab the node-sonos-http-api - the instructions say use the following command "npm install https://github.com/jishi/node-sonos-http-api"  
      • Tried it and it failed spectacularly (tons of error and warning messages) 
      • One of the first errors was "Error: not found: git" - I guess I need git as well 
        • Download git from https://git-scm.com/download/win 
        • Install git 
        • I chose to use the windows cmd.exe and the rest were the defaults 
        • Tried it again - it errored off 
        • Remembered that I just installed git so my DOS window that was already open wouldn't have the new environment variables.  I closed the window and opened an new one 
        • Tried it again - SUCCESS 
          • There were some warnings but it worked!
  3. Take the node-sonos-http-api/presets.json that I have here and drop it into your node-sonos-http-api root directory. Modify it to use your speaker names and your favorite stations. Don't worry about the "uri" field - it's unused. Make sure the preset names are lowercase (like "test" and "rock" in my example). NOTE: You can skip this step if you only want to use Playlists and Favorites, which require no configuration.
    • OK - I found the directory on the website that referenced the presets.json file but, instead of downloading the file, it brought up the file text.
      • Fine - I copied the file text (control c is my friend)
    • Found the node-sonos-http-api root directory and created the presets.json file with the data I copied from the web page.
  4. Test it by hitting http://yourserverip:5005/zones
    • I opened up Internet Explorer and tested it out - it worked!!
  5. If you get a response, great! Now try playing something: http://yourserverip:5005/preset/[your_preset_name]. Or, play a Playlist or Favorite (example: http://yourserverip:5005/kitchen/playlist/myplaylist). To stop, use /pauseall.
    • Also worked!
  6. If you have problems, make sure you are on the same network as your Sonos AND make sure you don't have a Sonos client running on the same machine. The client can interfere with the node.js server.

Expose your server to the outside world

  1. You need some way for Lambda to contact your server consistently. Services like DynDns and yDNS.eu will give you a consistent hostname for the outside world to use. If you have an Asus router like I do, then dynamic DNS is actually a built-in / free feature.
    • I have an ASUS router so this should be easy - yay!
  2. On your local router, find the "Port Forwarding" configuration. Forward all inbound requests to 5005 (or configure some other port) to your node server.
    • Logged into router settings 
    • Set up a dns name for my connection (.asuscomm.com) 
    • WAN -> DDNS 
    • Set up port forwarding  
    • WAN -> VIRTUAL SERVER/PORT FORWARDING 
    • Tested it: http://.asuscomm.com:5005/kitchen/pauseall 
    • It worked!!
  3. Make sure your server has a locally static IP, so port forwarding doesn't lose track of it.
    • Did that.
  4. Setup your server to auto-start or daemonize the node-sonos-http-api server.
    • I did not do this as I don't intend for my desktop to be a server.  I am aware I will need to manually restart the application if I ever log out or reboot my desktop.
  5. Test it by hitting http://yourdyndnsaddress:5005/zones.
    • It worked!

Create the Alexa Skill that will send events to AWS Lambda

  1. Create a new Skill in the Alexa Skills control panel on Amazon. You need a developer account to do this. The account must be the same as bound to your Echo, and make sure you are logged into that account on amazon.com. You will get access denied if the two accounts are different.
    • I went to developer.amazon.com and signed up for a new account.
  2. Name can be whatever you want. "Invocation" is what you say (I used "Sonos").
    • I used Sonos as well - it's my Sonos after all
  3. Check Custom Interaction Model if it is not already checked. Click Next
  4. Click Next, taking you to Interaction Model. Create a Custom Slot Type ("Add Slot Type"). Add a new type for PRESETS, another for ROOMS, and a final one for TOGGLES. Into each, copy/paste the contents of echo/custom_slots/PRESETS.slot.txt, echo/custom_slots/ROOMS.slot.txt and echo/custom_slots/TOGGLES.slot.txt.
    • Here once again I copied the text of the files from the github site and pasted them into the appropriate boxes.
  5. Still in Interaction Model, copy this repo's echo/intents.json into the "Intent Schema" field, and echo/utterances.txt into "Sample Utterances".
    • More cutting and pasting.
  6. Don't test yet, just save. Click back to "Skill Information" and copy the "Application ID". You'll need this for Lambda.

Configure the AWS Lambda service that will trigger your node-sonos-http-api server

  1. Create an AWS Lambda account if you don't have one already. It's free!
    • It free! but Amazon still requires a credit card number so they can charge you for any non-free services.  That almost did it for me but I had come this far and I wanted to finish it.
  2. In the Lambda console, look to the upper right. Make sure "N. Virginia" is selected, because not every zone supports Alexa yet.
    • He's not kidding here.  I picked one in the western US and it didn't work at all, I had to recreate my whole service in the N. Virginia zone.
  3. Create a new Lambda function. Skip the blueprint.
  4. Pick any name you want, and choose runtime Node.js.
  5. Go into this repo's lambda/src directory and copy options.example.js to options.js. Edit options.js to have your DynDNS hostname, your port, and the Alexa App ID you just copied.
    • Created a directory on my desktop and copied the text into the options.js file.
    • Made the updates as directed.
  6. In lambda/src, zip up everything. On Mac/Linux, cd src; chmod a+r *.js; zip src.zip *.js. Make sure you don't capture the folder, just the files.
    • Once again, I cut and pasted the text from the github source into the files on my desktop.
  7. Choose to upload the zip file for src.zip.
  8. The default handler is fine. Create a new role of type Basic Execution Role. Pick smallest possible memory and so on.
    • The role options have changed.  I just picked custom role and named it something.  I don't know if that was right or not but it let me continue.
  9. Click Next to proceed. Once created, click "Event Sources".
    • This has been changed to triggers.  It took me a while to figure that out.  I actually tried skipping it at first but the thing wouldn't work.
  10. Add a source. Choose "Alexa Skills Kit".
  11. Test it out. I included a test blueprint in this repo. Click "Test" and copy/paste this repo's lambda/play_intent_testreq.json to test. It will trigger the "test" preset in your presets.json file on your Sonos server. Don't forget to replace the Alexa App Id again.

Connect Alexa Skill to AWS Lambda

  1. In the Lambda console, copy the long "ARN" string in the upper right.
  2. Go back into the Alexa Skill console, open your skill, click "Skill Information", choose Lambda ARN and paste that ARN string in.
  3. Now you're ready to put it all together. Try "Alexa, use Sonos to play test"
And we're done!!  It's going to be so awesome!!  I try it out: "Alexa, tell Sonos to pause all" - It work's!!  "Alexa, tell Sonos to play playlist My Favorites in the Kitchen" - nothing.  "Alexa, tell Sonos to play favorite My Favorites in the Kitchen" - nothing.

OK - I can play and pause but I can't play playlists or favorites.  Not cool.

I do some research and dig through forums.  OK - it looks like it doesn't work right on node.js versions higher than 4.x and I have version 6.3.1. 

So I uninstall node.js 6.3.1 and install version 4.4.7 and restart the server.

Now it works!  Well, mostly.  It has a hard time with multi-word playlists and almost all of my playlists are multi-word.  Well, apart from that, it works.

I'm going to play around with it for a while and, if I can get it working like I want, I'll get a raspberry pi computer and run the whole thing from there.  More to come...

1 comment:

Jeff said...

I have successfully transferred my Sonos controller from my Windows PC to a Raspberry Pi PC. Read how I did it here:

http://howastute.blogspot.com/2016/08/sonos-control-with-amazon-echo-part-2.html