Wednesday, August 14, 2013

Yamaha Network Control

During my home automation project, where I want to use Raspberry Pi running lighttpd to make web for home automation, I've learned a bit about Yamaha network control and because it took me some time to find the info. I would like to share my findings.
There are two ways you might control this device:
  • UPnP, which doesn't seem to work properly as far any attempt to play music from computer failed.
  • YNC (Yamaha network control), unfortunatly I didn't find any official documentation about this protocol from Yamaha, however devices hold this documentation them self, it's basically extension of UPnP.

Bellow is part of UPnP specification showing controlURL and SCPDURL, SCPDURL is path xml definition of function provided by the service. (More on UPnP here)
<yamaha:X_device>
  <yamaha:X_serviceList>
    <yamaha:X_service>
      <yamaha:X_specType>urn:schemas-yamaha-com:service:X_YamahaRemoteControl:1</yamaha:X_specType>
      <yamaha:X_controlURL>/YamahaRemoteControl/ctrl</yamaha:X_controlURL>
      <yamaha:X_unitDescURL>/YamahaRemoteControl/desc.xml</yamaha:X_unitDescURL>
    </yamaha:X_service>
  </yamaha:X_serviceList>
</yamaha:X_device> 
I also find that somebody collect those and transform them into excel file which, might be more readble here
Here is backup in cause the original link doesn't work.
Based on the specification in picture we created the sample message to increase volume level

<YAMAHA_AV cmd="PUT">
  <Main_Zone>
    <Volume>
      <Lvl>
        <Val>Up 1 dB</Val>
        <Exp></Exp>
        <Unit></Unit>
      </Lvl>
    </Volume>
  </Main_Zone>
</YAMAHA_AV>
And here is piece of python code to send it.
#! /usr/bin/python

import httplib, urllib
import re

conn = httplib.HTTPConnection("10.0.0.36:80")
xml_data='<YAMAHA_AV cmd="PUT"><Main_Zone><Volume><Lvl><Val>Up 1 dB</Val><Exp></Exp><Unit></Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>'

params = urllib.urlencode({'q': 'set'})
headers = { 'Content-Type': 'application/xml', "Content-Length": "%d" % len(xml_data)}

conn.request("POST", "/YamahaRemoteControl/ctrl", "", headers)
conn.send(xml_data)

response = conn.getresponse()

#FOR DEBUG
#print response.status, response.reason
#print response.read()

conn.close()
As far as YNC returns XML you can use CSS to display the information return on web, below is python code requesting information about media curently played. As well as CSS code to display it.

HTR-4065_PlayInfo.py
#! /usr/bin/python

import sqlite3
import sys
import httplib, urllib
import re
from xml.dom import minidom
from xml.parsers.expat import ExpatError

conn = httplib.HTTPConnection("10.0.0.36:80")
xml_play_info = '<?xml version="1.0" encoding="utf-8"?><YAMAHA_AV cmd="GET"><SERVER><Play_Info<GetParam</Play_Info></SERVER></YAMAHA_AV>'


params = urllib.urlencode({'q': 'set'})
headers = { 'Content-Type': 'application/xml', "Content-Length": "%d" % len(xml_play_info)}

conn.request("POST", "/YamahaRemoteControl/ctrl", "", headers)
conn.send(xml_play_info)

response = conn.getresponse()

#print response.status, response.reason
response_data = response.read()
print response_data

Here is the response
<YAMAHA_AV rsp="GET" RC="0">
  <SERVER>
    <Play_Info>
      <Feature_Availability>Ready</Feature_Availability>
      <Playback_Info>Play</Playback_Info>
      <Play_Mode>
        <Repeat>Off</Repeat>
        <Shuffle>Off</Shuffle>
      </Play_Mode>
      <Meta_Info>
        <Artist>Enya</Artist>
        <Album>Paint The Sky With Stars</Album>
        <Song>Boadicea</Song>
      </Meta_Info>
      <Album_ART>
        <URL>/YamahaRemoteControl/AlbumART/AlbumART.ymf</URL>
        <ID>243</ID>
        <Format>YMF</Format>
      </Album_ART>
    </Play_Info>
  </SERVER>
</YAMAHA_AV>
yncPlayInfo.css
Feature_Availability, Album_ART, Playback_Info {display: none}
Meta_Info, Play_Mode {display: block}
Meta_Info:before {content: "Currently Playing:"; font-weight: bold}
Play_Mode:before {content: "Play mode:"; font-weight: bold}
Artist, Album, Song {display: list-item; list-style-type: none}
Artist:before {content: "Artist: "; margin-left: 10px; font-weight: bold}
Album:before {content: "Album: "; margin-left: 10px; font-weight: bold}
Song:before {content: "Song: "; margin-left: 10px; font-weight: bold}
Repeat, Shuffle {display: list-item}
Repeat:before {content: "Repeat: "; margin-left: 10px; font-weight: bold}
Shuffle:before {content: "Shuffle: "; margin-left: 10px; font-weight: bold}

menu_status, menu_layer, attribute {display: none}
menu_name {font-weight: bold}
current_list {display: block}
txt {display: list-item; list-style-type: none; margin-left: 10px}