Licht steuern mit Java und Philips Hue
Motivation
Für meinen selbstprogrammierten Sprachassistenten wollte ich ein Modul für die Lichtsteuerung entwickeln. Ziel war es, sowohl Dimmen als auch Farben und verschiedene Szenen durch Sprache aufzurufen und ohne weitere Tätigkeit anzuwenden.In diesem Blogpost möchte ich einen Teil davon vorstellen. Hier wird das Ein- und Ausschalten, sowieso das Setzen von Farben und verschiedenen Szenen beschrieben. Begonnen wird mit dem Generieren des API Schlüssels, gefolgt vom zugehörigen Programmcode. All dies geschieht mit Philips Hue Lampen und klassischem Java.
Benötigte Komponenten
- Philips HUE Lampen und Bridge, ich haben eines der Startersets verwendet- dort ist all das enthalten.
- Java ab Version 8
Developer Key Generieren
Nachdem die Bridge angeschlossen und betriebsbereit ist (einfach der beiliegenden Anleitung folgen), erfolgt im nächsten Schritt die Generierung des API Keys. Zunächst sollte die Bridge über den Browser aufgerufen werden. Dies ist relativ einfach über nachfolgende URL durchzuführen: https://discovery.meethue.com.
Sollte dies aus irgendwelchen Gründen nicht funktionieren, besteht die Möglichkeit über den Router und die angeschlossenen Geräte die IP zu ermitteln.
Nachdem die IP erfolgreich ermittelt wurde, muss diese zum Testen in den Browser eintragen werden. Nun sollten wir uns auf der lokalen Bridge befinden. Dies sieht in etwa so aus:
Als nächstes rufen wir die Developer Konsole über nachfolgenden Link auf:
https://<bridge ip address>/debug/clip.html
Über diese Konsole können bereits erste Befehle abgesetzt und die entsprechenden Antworten eingeholt werden. Mit diesem Mechanismus lässt sich auch der benötigte API Key generieren. Zuerst muss ein Benutzer authorisiert werden, andernsfalls erscheint die Antwortmeldung: unauthorised user.
URL /api
Body {"devicetype":"my_hue_app#my_user"}
Method POST
Mit den oben genannten Parametern teilen wir unserer Hue Bridge mit, eine neue Applikation mit einem neuen Benutzer zu registrieren.
Nach dem Ausführen des Befehls erscheint zunächst eine Fehledermeldung "link button not pressed". Dies diehnt als Sicherheitsmaßnahme. Um den Schritt abzuschließen einfach den großen Knopf an der Bridge drücken und anschließend den Befehl erneut absenden.
Dannach sollte auch schon der API Key im Command Response Feld zu sehen sein.
"Es werde Licht"
Nachfolgend wird der Javacode und die benötigten Befehle zur Steuerung vorgestellt. Alle Anweisungen werden via JSON gesendet bzw. Empfangen und verarbeitet.
Licht ein- und ausschalten
Json Kommando
Address https://<bridge ip address>/api/APIKEY/lights/1/state
Body {"on":true}
Method PUT
Mit den oben genannten Parametern lässt sich die Lampe ein (true) und wieder ausschalten (false). Da es sich um einen PUT Request handelt, wird dies ebenfalls über die Methodenart mitgeteilt. Die Ziffer nach /lights/ zeigt, an welche Lampe der Befehl gerichtet ist.
Java Code
Als erstes wird der Basis-Zugriffspfad für die Bridge gesetzt. Die URL sieht in etwa so aus:
http://192.xxx.xxx.xx/api/YOUR-API-KEY
public HueBridge(String base){
this.base = base;
}
Als nächstes benötigen wir eine Setup Methode, um die JSON Verbindung aufzubauen. URL ist eine Kombination aus der base und des weiteren JSON Kommandos. Method steht hier für die Art des Requests, in unserem Fall PUT oder GET.
private HttpURLConnection setupConnection(URL url, String method) throws IOException, ProtocolException {
HttpURLConnection connection = ( HttpURLConnection ) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod(method);
connection.setRequestProperty("Content-Type", "application/json");
return connection;
}
Jetzt sind wir schon in der Lage diese Verbindung für eine Set-Methode zu nutzen.
private void setState(String json, URL url) throws IOException, ProtocolException, HueException {
HttpURLConnection connection = null;
try {
connection = setupConnection(url, "PUT");
sendJsonCommand(json, connection);
handleStatusException(connection.getResponseCode());
JsonReader jsonReader = Json.createReader(connection.getInputStream());
JsonArray jsonArray = jsonReader.readArray();
jsonReader.close();
JsonObject jsonObject = jsonArray.getJsonObject(0);
handleBridgeException(jsonObject);
} finally {
if (connection != null)
connection.disconnect();
}
}
private void handleBridgeException(JsonObject object) throws HueException {
if (object.getJsonObject("success") == null)
throw new HueException("Bridge returned an error: " + object);
}
private void handleStatusException(int status) throws HueException {
if (status != HttpURLConnection.HTTP_OK)
throw new HueException("Bridge returned status " + status);
}
private void sendJsonCommand(String json, HttpURLConnection connection) throws IOException {
OutputStreamWriter os = new OutputStreamWriter(connection.getOutputStream());
os.write(json);
os.close();
}
Die setState- Methode ermöglicht JSON Kommandos an die HUE Bridge zu senden. Da an dieser Stelle auch der ein oder andere Fehler auftauchen kann, wird in den entsprechenden Exception-Handlern darauf eingegangen. Eine Verbindung wird aufgebaut, die Anfrage gelesen und die Antwort der Bridge ausgewertet. Hier werden ausschließlich PUT-Requests behandelt. Für GET-Request ist eine weitere Methode notwendig.
private JsonObject getState(URL url) throws IOException, ProtocolException, HueException {
HttpURLConnection connection = null;
JsonObject jsonObject = null;
try {
connection = setupConnection(url, "GET");
handleStatusException(connection.getResponseCode());
JsonReader jsonReader = Json.createReader(connection.getInputStream());
jsonObject = jsonReader.readObject();
jsonReader.close();
return jsonObject;
} finally {
if (connection != null)
connection.disconnect();
}
}
Mit der getState-Methode lassen sich nun auch Abfragen an die Bridge stellen und deren Ergebnis auswerten. Dies ist z.B. für das Auswerten vorhandener Szenen notwendig.
Mit der nachfolgenden Methode lässt sich bereits das Licht ein-und ausschalten.
public void setLampState(int lamp, String json) throws IOException, HueException {
URL url = new URL(base + "/lights/" + Integer.toString(lamp) + "/state");
setState(json, url);
}
Im Anschluss das Anwenden dieser Methode:
HueBridge bridge = new HueBridge(YOURBRIDGEURL);
bridge.setLampState(1, "{"on":false}");
"Was ist deine Lieblingsfarbe? – Bunt"
Die oben beschriebene Class lässt sich mühelos zur Farbsteuerung anwenden. Voraussetzung hierfür ist die Verwendung einer HUE Birne.
Farbe ändern
Zum Verändern der Farbe verwenden wir die gleichen Methoden wie auch schon beim Licht ein- bzw. ausschalten. Allerdings wird zusätzlich noch der xy-Wert gesetzt. Bei diesem Wert handelt es sich um einen CIE-Color-Wert. Beispiel: Der Wert {0.55,0.45} stellt die Farbe Gelb da.
HueBridge bridge = new HueBridge(YOURBRIDGEURL);
//Gelb
bridge.setLampState(1, "{"xy":[0.55,0.45]}");
//Blau
bridge.setLampState(1, "{"xy":[0.0,0.0]}");
//Grün
bridge.setLampState(1, "{"xy":[0.260,0.9972]}");
//Rot
bridge.setLampState(1, "{"xy":[0.8,0.0]}");
"Die richtige Szene"
Über die HUE-App lassen sich verschiedene Szenen hinterlegen. Unter Anderem wird darüber nicht nur Lichtfarbe, sondern auch Intensität gesteuert und gespeichert. Diese vorhandenen Szenen lassen sich auslesen und entsprechend laden. Folgendes ist hier zu beachten: Beim Auslesen erhalten wir die Szenen ID und nicht den eingespeicherten Namen.
Vorhandene Szenen auslesen und laden
Unter Verwendung der getState-Methode lässt sich eine weitere Methode schreiben, um alle vorhandenen Szenen zu laden.
public JsonObject getAllScenes() throws ProtocolException, IOException, HueException {
URL url = new URL(base + "/scenes/");
return getState(url);
}
Wie bereits erwähnt, erhalten wir hier nur die IDs. Da Szenen IDs recht unhandlich und wenig aussagekräftig sind, ist es besser die Szene über ihren richtigen Namen zu laden.
public String getSceneIdByName(String name) throws ProtocolException, IOException, HueException {
JsonObject allScenes = getAllScenes();
Set<String> allSceneIds = allScenes.keySet();
for (String sceneId : allSceneIds) {
JsonObject sceneDetailsById = allScenes.getJsonObject(sceneId);
JsonString sceneName = sceneDetailsById.getJsonString("name");
if (sceneName.getString().toLowerCase().equals(name.toLowerCase())) {
return sceneId.toString();
}
}
return null;
}
Mit dieser Methode lässt sich die Szenen ID zum entsprechenden Namen auslesen. Angewendet sieht dies folgendermaßen aus:
public void setScene(String groupId, String json) throws IOException, HueException {
URL url = new URL(base + "/groups/" + groupId + "/action");
setState(json, url);
}
String sceneId = bridge.getSceneIdByName(sceneName);
bridge.setScene(NAMEDESZIMMERS, "{"scene":"" + sceneId + ""}");
Weitere Möglichkeiten und Informationen
Die HUE Bridge und deren API kann noch vieles mehr, das oben Gezeigte ist nur ein kleiner Einblick. Auch Weckfunktionen und Timer sind möglich. Da diese API leicht über JSON verwendbar ist, eignet sie sich auch hervorragend für andere Programmiersprachen und natürlich andere Applikationen neben Sprachassistenten.
Bei Fragen stehe ich gerne per Mail zur Verfügung.