/**
* Copyright (c) 2010-2016, openHAB.org and others.
*
* 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.panasonictv.internal;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.panasonictv.PanasonicTVBindingConfig;
import org.openhab.binding.panasonictv.PanasonicTVBindingProvider;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.Command;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class in mainly used for receiving internal command and to send them to
* the Panasonic TV.
*
* @author André Heuer
* @since 1.7.0
*/
public class PanasonicTVBinding extends AbstractBinding<PanasonicTVBindingProvider>implements ManagedService {
private Map<String, String> registeredTVs = new HashMap<String, String>();
private static final Logger logger = LoggerFactory.getLogger(PanasonicTVBinding.class);
/**
* the refresh interval which is used to poll values from the PanansonicTV
* server (optional, defaults to 60000ms)
*/
private long refreshInterval = 60000;
/**
* Listening port of the TV
*/
private final int tvPort = 55000;
public PanasonicTVBinding() {
}
@Override
public void activate() {
}
@Override
public void deactivate() {
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
logger.debug("internalReceiveCommand() for item: " + itemName + " with command: " + command.toString());
if (this.providers.isEmpty()) {
logger.error("Binding is properly configured or loaded. No provider was found.");
return;
}
for (PanasonicTVBindingProvider provider : this.providers) {
PanasonicTVBindingConfig config = provider.getBindingConfigForItem(itemName);
if (config == null) {
continue;
}
int response = sendCommand(config);
if (response != 200) {
logger.warn("Command " + config.getCommand() + " to TV with IP " + registeredTVs.get(config.getTv())
+ " failed with HTTP Reponse Code " + response);
continue;
}
eventPublisher.postUpdate(itemName, OnOffType.OFF);
}
}
protected void addBindingProvider(PanasonicTVBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(PanasonicTVBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* {@inheritDoc}
*/
@Override
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
if (config != null) {
String refreshIntervalString = (String) config.get("refresh");
if (StringUtils.isNotBlank(refreshIntervalString)) {
refreshInterval = Long.parseLong(refreshIntervalString);
}
for (Enumeration<?> e = config.keys(); e.hasMoreElements();) {
String tv = (String) e.nextElement();
if (tv.equalsIgnoreCase("service.pid") || tv.equalsIgnoreCase("refresh")) {
continue;
}
logger.info("TV registered '" + tv + "' with IP '" + config.get(tv) + "'");
registeredTVs.put(tv, config.get(tv).toString());
}
if (registeredTVs.isEmpty()) {
logger.debug("No TV was registered in config file");
}
}
}
/**
* This methods sends the command to the TV
*
* @return HTTP response code from the TV (should be 200)
*/
private int sendCommand(PanasonicTVBindingConfig config) {
String command = config.getCommand().toUpperCase();
final String soaprequest_skeleton = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+ "<s:Body><u:X_SendKey xmlns:u=\"urn:panasonic-com:service:p00NetworkControl:1\">"
+ "<X_KeyEvent>NRC_%s</X_KeyEvent></u:X_SendKey></s:Body></s:Envelope>\r";
String soaprequest = "";
if (config.getCommand().toUpperCase().startsWith("HDMI")) {
soaprequest = String.format(soaprequest_skeleton, command);
} else {
soaprequest = String.format(soaprequest_skeleton, command + "-ONOFF");
}
String tvIp = registeredTVs.get(config.getTv());
if ((tvIp == null) || tvIp.isEmpty()) {
return 0;
}
try {
Socket client = new Socket(tvIp, tvPort);
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(), "UTF8"));
String header = "POST /nrc/control_0/ HTTP/1.1\r\n";
header = header + "Host: " + tvIp + ":" + tvPort + "\r\n";
header = header + "SOAPACTION: \"urn:panasonic-com:service:p00NetworkControl:1#X_SendKey\"\r\n";
header = header + "Content-Type: text/xml; charset=\"utf-8\"\r\n";
header = header + "Content-Length: " + soaprequest.length() + "\r\n";
header = header + "\r\n";
String request = header + soaprequest;
logger.debug("Request send to TV with IP " + tvIp + ": " + request);
wr.write(header);
wr.write(soaprequest);
wr.flush();
InputStream inFromServer = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inFromServer));
String response = reader.readLine();
client.close();
logger.debug("TV Response from " + tvIp + ": " + response);
return Integer.parseInt(response.split(" ")[1]);
} catch (IOException e) {
logger.error("Exception during communication to the TV: " + e.getStackTrace());
} catch (Exception e) {
logger.error("Exception in binding during execution of command: " + e.getStackTrace());
}
return 0;
}
}