I have struggled for a while with how to switch on/off my Zone B speakers on my Yamaha RX-573 and thought I’d share what I went through to get this working. It seems other Yamaha receivers have separate zones that can have different source inputs but the RX-573 is quite simple. Just an additional pair of ‘Zone B’ speakers. There is a button on the remote for choosing the active zone(s) but no option in the AV Yamaha Receiver app for IOS. My first ‘hack’ I discovered was that I could control the zone by defining different ‘scenes’. In my case the three scenes were:
-
All speakers on (Zone A + Zone B)
-
Living room only (Zone A only)
-
Kitchen (Zone B only)
This works very well but with a couple of drawbacks. When I want to switch speaker configuration I have to open the AV Yamaha app and choose the scene I want (slightly inconvenient). Secondly, when configuring a scene you set its source input so all my scenes were input source ‘Airplay’. This is fine if I am airplaying, however if I am using Chromecast and switch my scene, the input goes to ‘Airplay’ and I have to manually switch the source back to HDMI4 (Chromecast).
After living with this for a couple of years, I started investigating Home Assistant. I don’t have a Raspberry PI and before investing the ~$80 in a PI kit, I thought I’d get everything working from my Windows PC.
After looking at the Yamaha xml discovery (http://myIP_for_receiver/YamahaRemoteControl/desc.xml) , it became apparent that there was no magical ‘Zone A/Zone B’ selection capability in the API. What I could do though was use the Scene selection and improve on it.
I looked at how I could send a ‘set scene’ command. I found curl useful for testing:
curl -d "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Scene><Scene_Sel>Scene 2</Scene_Sel></Scene></Main_Zone></YAMAHA_AV>" http://192.168.2.100/YamahaRemoteControl/ctrl
I considered this as a ‘shell command’ but some python would do this better.
I needed a script that could query the receiver for its currently selected source, set the scene to give the desired speaker configuration and then finally send a ‘set source’ command to put the receiver back to its original source. There might be a better way to do this, but I settled on a python script. Initially I looked at the Home Assistant ‘python scripts’ capability (https://home-assistant.io/components/python_script/) but the big drawback there is there is no way to ‘import’. I need xml parsing capability which requires import xml.etree.ElementTree. Therefore, I decided to go with a Home Assistant ‘Shell Command’ (https://home-assistant.io/components/shell_command/). One benefit of using this approach was that I could test and debug the python script at my command line, outside of HA. Once I had the script working correctly, I had to integrate with HA. I placed my setScene script in my home directory (not the .homeassisstant directory). In Windows this was at c:\users\Andy. I then defined three ‘scripts’ that could call the shell commands. These scripts make it possible to activate the shell commands from the HA app.
Excerpt of configuration.yaml
media_player:
- platform: yamaha
host: 192.168.2.100
name: 'Living Room Receiver'
source_names:
HDMI2: "TV"
HDMI4: "ChromeCast"
HDMI3: "Apple TV"
HDMI1: "Blu Ray"
attributes:
volume_level: media_player.living_room_receiver|volume_level
source: media_player.living_room_receiver|source
source_list: media_player.living_room_receiver|source_list
shell_command:
scene_1_allspeakers: 'python setscene.py "Scene 1"'
scene_2_lrspeakers: 'python setscene.py "Scene 2"'
scene_3_kitchenspeakers: 'python setscene.py "Scene 3"'
My setScene py script:
# Python script to receive a parameter with the desired scene for Yamaha receiver to switch to.
# Input source is also retrieved and set.
import sys
import time
import requests
from xml.etree import ElementTree as ET
logger.info("setScene")
# get command line argument
newScence =sys.argv[1] # Script parameter is desired Scene. Example: 'Scene 1'
# The receiver only understands 'Scene 1' 'Scene 2' etc.
logger.info(newScence)
# this is the xml sent to receiver to query for current selected source
getSelected = '<YAMAHA_AV cmd=\"GET\"><Main_Zone><Input><Input_Sel>GetParam</Input_Sel></Input></Main_Zone></YAMAHA_AV>'
# URL with IP of my Yamaha receiver. Receiver will accept POST control commands, with xml data for specific function.
url = 'http://192.168.2.100/YamahaRemoteControl/ctrl'
# get current selected source
r = requests.post(url, data=getSelected, headers='')
SourceInput = ET.fromstring(r.content).find('.//Main_Zone/Input/Input_Sel')
print ('Source is =', SourceInput.text)
print ('setting scene to ' + newScence)
#Set the scene
setScencepayload = "<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Scene><Scene_Sel>" + newScence + "</Scene_Sel></Scene></Main_Zone></YAMAHA_AV>"
r = requests.post(url, data=setScencepayload, headers='')
# set input back, since it was trounced by scene selection
print ('Setting Source')
setSource = '<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Input><Input_Sel>' + SourceInput.text + '</Input_Sel></Input></Main_Zone></YAMAHA_AV>'
r = requests.post(url, data=setSource, headers='')
Google Home
Once this was done the next step was to integrate with Google Home. I configured my router to allow port forwarding and then setup my IP with duckdns dot org. Setting up the Google Home logic was relatively simple with IFTTT, although it took me a few minutes to figure out the IFTTT interface (you have to click on ‘This’ and then ‘That’…duh!). My ‘this’ was of course the Google Assistant ‘Say a simple phrase’ with a phrase like ‘turn on Kitchen Speakers’. My ‘That’ was a webhooks call:
http://mydomain.duckdns.org:8123/api/services/shell_command/scene_1_allspeakers?api_password=mypassword
I created three IFTTT applets, one for each of my three ‘scenes’.
I realize this is insecure without https but I am still in ‘proof of concept’ so next step is to order my Raspberry PI and get encryption configured.