/**
* 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.ecotouch.internal;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Network communication with Waterkotte EcoTouch heat pumps.
*
* The communication protocol was reverse engineered from the Easy-Con Android
* app. The meaning of the EcoTouch tags was provided by Waterkotte's technical
* service (by an excerpt of a developer manual).
*
* @author Sebastian Held <sebastian.held@gmx.de>
* @since 1.5.0
*/
public class EcoTouchConnector {
private String ip;
private String username;
private String password;
List<String> cookies;
static Pattern response_pattern = Pattern.compile("#(.+)\\s+S_OK[^0-9-]+([0-9-]+)\\s+([0-9-]+)");
private static final Logger logger = LoggerFactory.getLogger(EcoTouchConnector.class);
/**
* Create a network communication without having a current access token.
*/
public EcoTouchConnector(String ip, String username, String password) {
this.ip = ip;
this.username = username;
this.password = password;
this.cookies = null;
}
/**
* Create a network communication with access token. This speeds up
* retrieving values, because the log in step is omitted.
*/
public EcoTouchConnector(String ip, String username, String password, List<String> cookies) {
this.ip = ip;
this.username = username;
this.password = password;
this.cookies = cookies;
}
private void login() {
cookies = null;
String url = null;
try {
url = "http://" + ip + "/cgi/login?username=" + URLEncoder.encode(username, "UTF-8") + "&password="
+ URLEncoder.encode(password, "UTF-8");
URL loginurl = new URL(url);
URLConnection connection = loginurl.openConnection();
cookies = connection.getHeaderFields().get("Set-Cookie");
} catch (MalformedURLException e) {
logger.debug("The URL '" + url + "' is malformed: " + e.toString());
} catch (Exception e) {
logger.debug("Cannot log into Waterkotte EcoTouch: " + e.toString());
}
}
/**
* Request a value from the heat pump-
*
* @param tag
* The register to query (e.g. "A1")
* @return value This value is a 16-bit integer.
*/
public int getValue(String tag) throws Exception {
// request values
String url = "http://" + ip + "/cgi/readTags?n=1&t1=" + tag;
StringBuilder body = null;
int loginAttempt = 0;
while (loginAttempt < 2) {
try {
URLConnection connection = new URL(url).openConnection();
if (cookies != null) {
for (String cookie : cookies) {
connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
}
InputStream response = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(response));
body = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
body.append(line + "\n");
}
if (body.toString().contains("#" + tag)) {
// succeeded
break;
}
// s.th. went wrong; try to log in
throw new Exception();
} catch (Exception e) {
login();
loginAttempt++;
}
}
if (body == null || !body.toString().contains("#" + tag)) {
// failed
logger.debug("Cannot get value for tag '" + tag + "' from Waterkotte EcoTouch.");
throw new Exception("invalid response from EcoTouch");
}
// ok, the body now contains s.th. like
// #A30 S_OK
// 192 223
Matcher m = response_pattern.matcher(body.toString());
boolean b = m.find();
if (!b) {
// ill formatted response
logger.debug("ill formatted response: '" + body + "'");
throw new Exception("invalid response from EcoTouch");
}
return Integer.parseInt(m.group(3));
}
/**
* Set a value
*
* @param tag
* The register to set (e.g. "A1")
* @param value
* The 16-bit integer to set the register to
* @return value This value is a 16-bit integer.
*/
public int setValue(String tag, int value) throws Exception {
// set value
String url = "http://" + ip + "/cgi/writeTags?returnValue=true&n=1&t1=" + tag + "&v1=" + value;
StringBuilder body = null;
int loginAttempt = 0;
while (loginAttempt < 2) {
try {
URLConnection connection = new URL(url).openConnection();
if (cookies != null) {
for (String cookie : cookies) {
connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
}
InputStream response = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(response));
body = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
body.append(line + "\n");
}
if (body.toString().contains("#" + tag)) {
// succeeded
break;
}
// s.th. went wrong; try to log in
throw new Exception();
} catch (Exception e) {
login();
loginAttempt++;
}
}
if (body == null || !body.toString().contains("#" + tag)) {
// failed
logger.debug("Cannot get value for tag '" + tag + "' from Waterkotte EcoTouch.");
throw new Exception("invalid response from EcoTouch");
}
// ok, the body now contains s.th. like
// #A30 S_OK
// 192 223
Matcher m = response_pattern.matcher(body.toString());
boolean b = m.find();
if (!b) {
// ill formatted response
logger.debug("ill formatted response: '" + body + "'");
throw new Exception("invalid response from EcoTouch");
}
return Integer.parseInt(m.group(3));
}
/**
* Authentication token. Store this and use it, when creating the next
* instance of this class.
*
* @return cookies: This includes the authentication token retrieved during
* log in.
*/
public List<String> getCookies() {
return cookies;
}
}