da ja die XML Api keine Authentifizierung bietet, und ich die Nutzung der Portweiterleitung FritzBox -> CCU2 und der XML Api nicht der ganzen Welt anbieten möchte, habe ich einen kleinen Workaround erstellt. Vielleicht nicht neu, habe aber nichts derartiges gefunden.
Idee: Nutzung eines "Proxies", der die Authentifizierung übernimmt.
1. in Homedroid unter Konfiguration -> Experimental Settings den Punkt "Enable HTTP Auth" aktivieren und Benutzername und Passwort eintragen
2. in Homedroid unter Konfiguration -> Netzwerkeinstellungen den Punkt "Fernzugriff aktivieren" aktivieren, externe CCU-Adresse + Port eingeben (HTTPS ist nicht möglich)
3. Weiterleitung in der FritzBox einrichten auf einen Host im privaten Netzwerk, NICHT auf die CCU2 - in meinem Fall: ein kleiner Raspberry Pi
4. Auf dem Host für die Weiterleitung: Starten der Proxy Software (Quellcode s.u. - Verwendung und Modifizierung nach Lust und Laune... )
Code: Alles auswählen
java HomematicProxy 8088 homematic-ccu2 80 Proxyuser g3h3im!
Starte auf Port 8088, CCU unter homematic-ccu2:80
Benutzer: Proxyuser/Passwort: g3h3im!
Erwarte Verbindung...
...
Ist so sicher wie HTTP und Basic Authorisierung nun mal ist... aber besser als nichts
Viele Grüße von der alten Hippe.
Code: Alles auswählen
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
/**
*/
public class HomematicProxy {
/** default value */
static int PROXY_PORT = 8088;
/** default value */
static int CCU_PORT = 80;
/** default value */
static String CCU_HOSTNAME = "homematic-ccu2";
/** default value */
static String AUTHSTRING = "";
/**
* Creates a new instance of Proxy
* @param proxyPort
* @param ccuPort
* @param ccuHostName
* @param user
* @param password
*/
public HomematicProxy(int proxyPort, String ccuHostName, int ccuPort, String user, String password) {
PROXY_PORT = proxyPort;
CCU_PORT = ccuPort;
CCU_HOSTNAME = ccuHostName;
AUTHSTRING = javax.xml.bind.DatatypeConverter.printBase64Binary((user + ":" + password).getBytes());
System.out.println("Starte auf Port " + proxyPort + ", CCU unter " + ccuHostName + ":" + ccuPort);
System.out.println("Benutzer: " + user + "/Passwort: " + password);
int counter = 0;
while (true) {
try {
counter++;
ServerSocket ss = new ServerSocket(PROXY_PORT);
System.out.println("Erwarte Verbindung...");
Socket server = ss.accept();
System.out.println("Verbunden...!");
server.setSoTimeout(30 * 60 * 1000);
new Handler(server, server.getInputStream(), server.getOutputStream(), counter);
ss.close();
} catch (BindException be) {
System.err.println("Port " + PROXY_PORT + " bereits in Verwendung: " + be.getMessage());
System.exit(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private class Handler extends Thread {
BufferedInputStream clientIn;
BufferedOutputStream clientOut;
Socket clientSocket;
String name;
int connectionCounter = 0;
int reuseCounter = 0;
public Handler(Socket socket, InputStream in, OutputStream out, int counter) {
this.clientIn = new BufferedInputStream(in);
this.clientOut = new BufferedOutputStream(out);
this.connectionCounter = counter;
this.clientSocket = socket;
this.start();
}
public void run() {
this.name = "Verbindung " + connectionCounter + "/" + reuseCounter;
System.out.println(name + " START");
try {
BufferedReader clientInReader = new BufferedReader(new InputStreamReader(clientIn));
while (true) {
reuseCounter++;
this.name = "Verbindung " + connectionCounter + "/" + reuseCounter;
String requestString = "";
String s = null;
boolean authorized = false;
while ((s = clientInReader.readLine()) != null) {
if (s.startsWith("Authorization: ")) {
String auth = s.replace("Authorization: ", "");
if (auth.equals("Basic " + AUTHSTRING)) {
authorized = true;
System.out.println(name + " Authorisierung ok");
}
}
requestString += s;
requestString += "\r\n";
if (s.equals("")) {
break;
}
}
if (authorized) {
Socket serverSocket = new Socket(CCU_HOSTNAME, CCU_PORT);
BufferedWriter serverOutWriter = new BufferedWriter(new OutputStreamWriter(
serverSocket.getOutputStream()));
BufferedInputStream serverIn = new BufferedInputStream(serverSocket.getInputStream());
serverOutWriter.write(requestString);
System.out.println(name + " Request headers: " + requestString.replace("\r\n", ";"));
serverOutWriter.flush();
ByteArrayOutputStream responseHeaderBytes = new ByteArrayOutputStream();
int contentLength = 0;
while ((s = readLine(serverIn)) != null) {
if (s.startsWith("Content-Length: ")) {
contentLength = Integer.parseInt(s.replace("Content-Length: ", ""));
}
responseHeaderBytes.write((s + "\r\n").getBytes());
if (s.equals("")) {
break;
}
}
responseHeaderBytes.flush();
System.out.println(name + " Response headers: "
+ new String(responseHeaderBytes.toByteArray()).replace("\r\n", "; "));
clientOut.write(responseHeaderBytes.toByteArray());
clientOut.flush();
boolean waitForDisconnect = contentLength == 0;
int byteCount = 0;
try {
byte[] buf = new byte[4096];
int bytesIn = 0;
while (((byteCount < contentLength) || waitForDisconnect) && ((bytesIn = serverIn.read(buf)) >= 0)) {
clientOut.write(buf, 0, bytesIn);
clientOut.flush();
byteCount += bytesIn;
if (buf[bytesIn - 5] == 48 && buf[bytesIn - 4] == 13 && buf[bytesIn - 3] == 10
&& buf[bytesIn - 2] == 13 && buf[bytesIn - 1] == 10) {
// System.out.println(name + " Found terminating chunk.");
break;
}
}
clientOut.flush();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(name + " " + byteCount + " Bytes geschrieben - fertig.");
clientOut.flush();
serverOutWriter.close();
serverIn.close();
serverSocket.close();
} else {
clientOut
.write("HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\nContent-Length: 0\r\n\r\n"
.getBytes());
clientOut.flush();
System.out.println(name + " 401 NOT AUTHORIZED - Ende");
}
}
} catch (SocketTimeoutException e) {
System.out.println(name + " Verbindungsende wg. Timeout");
} catch (Exception e) {
System.out.println(name + " Verbindungsende: " + e.getMessage());
}
try {
clientSocket.close();
} catch (IOException e) {
System.err.println(name + " " + e.getMessage());
}
}
}
String readLine(InputStream in) {
StringBuffer buf = new StringBuffer("");
int c;
try {
in.mark(1);
if (in.read() == -1) {
return null;
} else {
in.reset();
}
while ((c = in.read()) >= 0) {
if ((c == 0) || (c == 10) || (c == 13)) {
break;
} else {
buf.append((char) c);
}
}
if (c == 13) {
in.mark(1);
if (in.read() != 10) {
in.reset();
}
}
} catch (Exception e) {
}
return buf.toString();
}
/**
* @param args
*/
public static void main(String[] args) {
if (null != args && args.length == 5) {
try {
new HomematicProxy(Integer.parseInt(args[0]), args[1], Integer.parseInt(args[2]), args[3], args[4]);
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
} else {
System.out
.println("5 Parameter benoetigt: <Port fuer Proxy> <Hostname CCU> <Port CCU> <Benutzer fuer Proxy> <Passwort>");
System.out.println("Beispiel: 8088 homematic-ccu2 80 ProxyUser g3he1m");
}
}
}