Sunday, September 16, 2018

Controlling Yamaha RX-V673 via web requests

So my last two posts have to do with setting up stuff so that I can use my new Google Home Mini's to control my stereo system. This post has to do with setting up a python webserver on my Kodi box so I can hit it from IFTTT and "forward" the requests to my amplifier. I have a Yamaha RX-X673, which has a web interface. I opened the web interface in my browser with http://ip-address, then the web interface appears. It's reasonably complete and looks like this:


Then I fired up Wireshark and captured the requests between my browser and the receiver. It turns out some idiot thought sending xml, rather than json, or even just regular key=value pairs via post, was a great idea. So, yeah, my amplifier has an xml parser built in. Who would have thought? It turns out that text/xml is not a content-type that I can use with  IFTTT Maker Webhooks, unless I've overlooked something.

Darn. This led me to set up a webserver on my Kodi box to take a request and then run a script per command to make the amplifier do its thing.

Setting up a webserver on Kodi is surprisingly easy, simply create a cgi-bin directory on it. Just ssh to it:

ssh root@ip-address

You'll end up in /storage by default, which is the right place to be.

Oh, wait, I must mention that I'm running openelec on my Kodi box. It's a dedicated box that sits inside my stereo cabinet. The default root password is "openelec" -- mine is not that anymore :)

I created a directory named "webserver", although this isn't strictly necessary. What is necessary is to create a directory named "cgi-bin", which I put inside the webserver directory. This is the place to put python scripts to execute.

The webserver itself is built into python, just

cd webserver
python -m CGIHTTPServer 8000

This starts a webserver at port 8000 and will run scripts contained in the cgi-bin directory. I set a port forward on my router so the IFTTT generated requests will be sent to that port on my Kodi box.

The scripts themselves are super simple, it's just does a wget call and returns a blank html page (which isn't at all necessary), so they look like this:

#!/usr/bin/env python
import subprocess

subprocess.call(['wget', '--post-data=<YAMAHA_AV cmd="PUT"><System><Power_Control><Power>On</Power></Power_Control></System></YAMAHA_AV>', 'http://ip-address/YamahaRemoteControl/ctrl'])

print("Content-type: text/html")

print("")

print("""<html><body></body></html>""")

This particular script will turn on the amplifier, and change the "ip-address" to what is appropriate.

All that is required to run other commands is to replace the --post-data with a different xml. Here are the ones that I used:

Power on:

<YAMAHA_AV cmd="PUT"><System><Power_Control><Power>On</Power></Power_Control></System></YAMAHA_AV>

Power off (actually standby):

<YAMAHA_AV cmd="PUT"><System><Power_Control><Power>Standby</Power></Power_Control></System></YAMAHA_AV>

Change input, change HDMI1 to what you need, I made multiple scripts to do this (HDMI1, HDMI2, AV1, etc), althought I'm going to go back and add some logic based on a text value passed from IFTTT eventually:

<YAMAHA_AV cmd="PUT"><Main_Zone><Input><Input_Sel>HDMI1</Input_Sel></Input></Main_Zone></YAMAHA_AV>

Set volume, this one is a little different in that I have IFTTT sending a volume value. I want to be able to also have a "volume up" and "volume down" command. For now, I'm using this script, in which I'll receive a "volume" query parameter as a number:

import subprocess
import cgi
import cgitb
cgitb.enable()

form = cgi.FieldStorage()

volume = form["volume"].value

# the minus before the volume is important, the amplifier has a volume range of
# -80.0 dB to 16.5 db, anything above 0 is way too loud for the house
command = '--post-data=<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Lvl><Val>-' + volume + '</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>'

subprocess.call(['wget', command, 'http://ip-address/YamahaRemoteControl/ctrl'])

print("Content-type: text/html")
print("")
print("<html><body>")
print("</body></html>")

Note the "-" sign before the volume number, the actual range of volume on the RX-V673 is -80.0 dB to 16.5 dB. Anything bigger than about -5 is way too loud, the usual range, for me at least, is in the -15 to -30 dB range. 

Mute on and off:

<YAMAHA_AV cmd="PUT"> <Main_Zone> <Volume> <Mute>On</Mute> </Volume> </Main_Zone> </YAMAHA_AV>'
<YAMAHA_AV cmd="PUT"> <Main_Zone> <Volume> <Mute>Off</Mute> </Volume> </Main_Zone> </YAMAHA_AV>'

That's it for now. I'll update this when I get the volume up and down commands working.

No comments: