/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.yamahareceiver.internal.hardware; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConfig.Zone; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; /** * Yamaha Receiver Proxy used to control a yamaha receiver with HTTP/XML * * @author Eric Thill * @author Ben Jones * @since 1.6.0 */ public class YamahaReceiverProxy { public static final int VOLUME_MIN = -80; public static final int VOLUME_MAX = 16; private final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); private final String host; public YamahaReceiverProxy(String host) { this.host = host; } public String getHost() { return host; } public void setPower(Zone zone, boolean on) throws IOException { if (on) { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Power_Control><Power>On</Power></Power_Control></" + zone + "></YAMAHA_AV>"); } else { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Power_Control><Power>Standby</Power></Power_Control></" + zone + "></YAMAHA_AV>"); } } public void setVolume(Zone zone, float volume) throws IOException { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Volume><Lvl><Val>" + (int) (volume * 10) + "</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></" + zone + "></YAMAHA_AV>"); } public void setMute(Zone zone, boolean mute) throws IOException { if (mute) { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Volume><Mute>On</Mute></Volume></" + zone + "></YAMAHA_AV>"); } else { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Volume><Mute>Off</Mute></Volume></" + zone + "></YAMAHA_AV>"); } } public void setInput(Zone zone, String name) throws IOException { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Input><Input_Sel>" + name + "</Input_Sel></Input></" + zone + "></YAMAHA_AV>"); } public void setSurroundProgram(Zone zone, String name) throws IOException { postAndGetResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><" + zone + "><Surround><Program_Sel><Current><Sound_Program>" + name + "</Sound_Program></Current></Program_Sel></Surround></" + zone + "></YAMAHA_AV>"); } public void setNetRadio(int lineNo) throws IOException { /* Jump to specified line in preset list */ postAndGetResponse( "<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"PUT\"><NET_RADIO><Play_Control><Preset><Preset_Sel>" + lineNo + "</Preset_Sel></Preset></Play_Control></NET_RADIO></YAMAHA_AV>"); } public YamahaReceiverState getState(Zone zone) throws IOException { Document doc = postAndGetXmlResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"GET\"><" + zone + "><Basic_Status>GetParam</Basic_Status></" + zone + "></YAMAHA_AV>"); Node basicStatus = getNode(doc.getFirstChild(), "" + zone + "/Basic_Status"); Node powerNode = getNode(basicStatus, "Power_Control/Power"); boolean power = powerNode != null ? "On".equalsIgnoreCase(powerNode.getTextContent()) : false; Node inputNode = getNode(basicStatus, "Input/Input_Sel"); String input = inputNode != null ? inputNode.getTextContent() : null; Node soundProgramNode = getNode(basicStatus, "Surround/Program_Sel/Current/Sound_Program"); String soundProgram = soundProgramNode != null ? soundProgramNode.getTextContent() : null; Node volumeNode = getNode(basicStatus, "Volume/Lvl/Val"); float volume = volumeNode != null ? Float.parseFloat(volumeNode.getTextContent()) * .1f : VOLUME_MIN; Node muteNode = getNode(basicStatus, "Volume/Mute"); boolean mute = muteNode != null ? "On".equalsIgnoreCase(muteNode.getTextContent()) : false; return new YamahaReceiverState(power, input, soundProgram, volume, mute); } public List<String> getInputsList(Zone zone) throws IOException { List<String> names = new ArrayList<String>(); Document doc = postAndGetXmlResponse("<?xml version=\"1.0\" encoding=\"utf-8\"?><YAMAHA_AV cmd=\"GET\"><" + zone + "><Input><Input_Sel_Item>GetParam</Input_Sel_Item></Input></" + zone + "></YAMAHA_AV>"); Node inputSelItem = getNode(doc.getFirstChild(), zone + "/Input/Input_Sel_Item"); NodeList items = inputSelItem.getChildNodes(); for (int i = 0; i < items.getLength(); i++) { Element item = (Element) items.item(i); String name = item.getElementsByTagName("Param").item(0).getTextContent(); boolean writable = item.getElementsByTagName("RW").item(0).getTextContent().contains("W"); if (writable) { names.add(name); } } return names; } private static Node getNode(Node root, String nodePath) { String[] nodePathArr = nodePath.split("/"); return getNode(root, nodePathArr, 0); } private static Node getNode(Node parent, String[] nodePath, int offset) { if (parent == null) { return null; } if (offset < nodePath.length - 1) { return getNode(((Element) parent).getElementsByTagName(nodePath[offset]).item(0), nodePath, offset + 1); } else { return ((Element) parent).getElementsByTagName(nodePath[offset]).item(0); } } private Document postAndGetXmlResponse(String message) throws IOException { String response = postAndGetResponse(message); String xml = response.toString(); try { DocumentBuilder db = dbf.newDocumentBuilder(); return db.parse(new InputSource(new StringReader(xml))); } catch (Exception e) { throw new IOException("Could not handle response", e); } } private String postAndGetResponse(String message) throws IOException { HttpURLConnection connection = null; try { URL url = new URL("http://" + host + "/YamahaRemoteControl/ctrl"); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Length", "" + Integer.toString(message.length())); connection.setUseCaches(false); connection.setDoInput(true); connection.setDoOutput(true); // Send request DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); wr.writeBytes(message); wr.flush(); wr.close(); // Read response InputStream is = connection.getInputStream(); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer response = new StringBuffer(); while ((line = rd.readLine()) != null) { response.append(line); response.append('\r'); } rd.close(); return response.toString(); } catch (Exception e) { throw new IOException("Could not handle http post", e); } finally { if (connection != null) { connection.disconnect(); } } } }