Controlling Sonos via a Google Home and Google Assistant

I just bought a Google Home Mini to play with while they're on a Black Friday offer.  They don't connect to Sonos by default yet, so I thought I'd try to do something about it ...

It wasn't an extremely quick job, but pretty painless nevertheless (on my Fedora 26 box).

 

Job 1:  command-line control of the Sonos

There's a Python library (soco) and command line (socos) which I've tinkered with in the past to do this.  (appropriate sudo prefixes presumed!)

  1. pip install soco
  2. pip install socos

Test it (note, socos won't take anything but IP addresses [see the Python script later]):

  1. echo -e "set SONOS_IP\nplay" | socos

You can see the commands with socos help.

 

Step 2:  CGI wrapper

Here's my script (put into /path/to/ifttt/sonos.py, see later), you can add custom amendments to the things that socos accepts, especially changing Sonos names to IP addresses.

It's a little over-complicated as I first thought it had to run multiple commands to socos to (e.g.) set the device and then pass a command, but most commands take an IP address as a final argument.  I've left it that way it is in case I need the flexibility in the future.

  1. #!/usr/bin/env python
  2. import cgi
  3. import fileinput
  4. import re
  5. import subprocess
  6. import sys
  7.  
  8. # Send a proper HTML response back to the client
  9. print "Content-Type: text/plain\r\n"
  10. print "\r\n"
  11.  
  12. # Read the commands and pass them to socos
  13. commands = 0
  14. proc = subprocess.Popen( ['socos'], shell=True,
  15. stdin=subprocess.PIPE, stdout=subprocess.PIPE )
  16. for line in fileinput.input():
  17. commands = commands+1
  18. for segment in line.strip().split('|'):
  19. # Custom amendments to what is said
  20. cmd = re.sub( " *\\bplease\\b *", "", segment \
  21. .replace('lounge','LOUNGE_IP') \
  22. .replace('play3','PLAY3_IP') \
  23. .replace('volume up','volume +25') \
  24. .replace('volume down','volume -25') \
  25. + "\n" )
  26. proc.stdin.write( cmd )
  27. proc.stdin.flush()
  28. proc.stdin.close();
  29.  
  30. # Flush output, removing socos prompts
  31. for line in iter( proc.stdout.readline, '' ):
  32. output = re.sub( "\\bsocos.*?>", "", line.rstrip() )
  33. if len(output):
  34. print output
  35.  
  36. # Done
  37. proc.terminate()
  38. if commands == 0:
  39. print( "No commands received" )
  40. exit(1)>

Test it:

  1. echo -e "set play3|status" | /path/to/ifttt/sonos.py

 

Step 3:  Web accessible

I'm not gonna help you get Apache running!  Once you have, this is the config I used:

# /etc/httpd/conf.d/ifttt.conf
Alias /ifttt /path/to/ifttt

 Options +FollowSymLinks +ExecCGI -Indexes
 AddHandler cgi-script .cgi .pl .py
 AuthType Basic
 AuthName "IFTTT"
 AuthUserFile /path/to/auth/iftt.auth
 Require valid-user
 AllowOverride All
 Order allow,deny
 Allow from all

Set up a password:

  1. htpasswd -c -b /path/to/auth/ifttt.auth USERNAME PASSWORD

“ifttt”?  You can see where I'm going ?.

And test the Python script again, but via Apache:

  1. echo -e "set play3\nstate" | curl -X POST --data-binary @- --basic \
  2.   https://USERNAME:PASSWORD@YOUR.DOMAIN/ifttt/sonos.py

 

Step 4:  IFTTT

Sign up to If This Then That (I used my Google login to make things easier).  Create an applet that uses Google Assistant (you'll have to link your IFTTT account with your Google one), and start with:

IF
Say a phrase with a text ingredient
What do you want to say?  sonos three $
What do you want the Assistant to say in response?  OK, telling Sonos the command $
THEN
Link to Webhooks

Make a web request
URL  https://USERNAME:PASSWORD@YOUR.DOMAIN/ifttt/sonos.py
Method  POST
Content Type  text/plain
Body  {{TextField}} play3

NB: alternative for more complex commands (and my original setting) — use the pipe to separate multiple socos commands:

Body  set play3|{{TextField}}

Step 5:  Profit!

Or not.  I think that's all I did (aside from wads of trial and error and diagnosing network issues, like I tried using Digest auth. not Basic, and that didn't work, or failing to POST multi-line commands, so having to add the fake pipe (|) separator.

You ought now to be able to say something like “Hey Google, Sonos Three pause” or  “Hey Google, Sonos Three volume up”.

I have a Sonos Play 3 (the above examples) called “play 3” and a Sonos Connect (called “lounge”) — I'll have to add another near identical IFTTT applet to control the lounge one.

Not a bad morning's work!


Advertisement


About fnx

PSN: the_phoenix

This entry was posted in Software, Tech and tagged , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*