From d337546da70ab27e5acc92d08f4d03acbedf0fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rennfanz?= Date: Sat, 9 Dec 2017 21:15:18 +0100 Subject: [PATCH] Add java part of plug-in for HomeWizard SmartPlug support --- .../bwssystems/HABridge/BridgeSettings.java | 1 + .../HABridge/BridgeSettingsDescriptor.java | 31 ++- .../bwssystems/HABridge/DeviceMapTypes.java | 2 + .../com/bwssystems/HABridge/HomeManager.java | 7 +- .../devicemanagmeent/DeviceResource.java | 6 + .../plugins/homewizard/HomeWizardHome.java | 151 ++++++++++++ .../homewizard/HomeWizardSmartPlugDevice.java | 47 ++++ .../homewizard/HomeWizzardSmartPlugInfo.java | 215 ++++++++++++++++++ .../plugins/homewizard/json/Device.java | 49 ++++ 9 files changed, 505 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardHome.java create mode 100644 src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardSmartPlugDevice.java create mode 100644 src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizzardSmartPlugInfo.java create mode 100644 src/main/java/com/bwssystems/HABridge/plugins/homewizard/json/Device.java diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index 611ec04..b8eb403 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -209,6 +209,7 @@ public class BridgeSettings extends BackupHandler { theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass()); theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz()); theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy()); + theBridgeSettings.setHomeWizardConfigured(theBridgeSettings.isValidHomeWizard()); // Lifx is either configured or not, so it does not need an update. if(serverPortOverride != null) theBridgeSettings.setServerPort(serverPortOverride); diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java index 4160d2f..6bb1108 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java @@ -93,7 +93,9 @@ public class BridgeSettingsDescriptor { @SerializedName("securityData") @Expose private String securityData; - + @SerializedName("homewizardaddress") + @Expose + private IpList homewizardaddress; private boolean settingsChanged; private boolean veraconfigured; @@ -107,7 +109,8 @@ public class BridgeSettingsDescriptor { private boolean domoticzconfigured; private boolean somfyconfigured; private boolean lifxconfigured; - + private boolean homewizardconfigured; + // Deprecated settings private String haltoken; private boolean upnpstrict; @@ -127,7 +130,7 @@ public class BridgeSettingsDescriptor { this.mqttconfigured = false; this.hassconfigured = false; this.domoticzconfigured = false; - this.somfyconfigured = false; + this.homewizardconfigured = false; this.lifxconfigured = false; this.farenheit = true; this.securityData = null; @@ -188,6 +191,9 @@ public class BridgeSettingsDescriptor { public IpList getSomfyAddress() { return somfyaddress; } + public IpList getHomeWizardAddress() { + return homewizardaddress; + } public void setVeraAddress(IpList veraAddress) { this.veraaddress = veraAddress; } @@ -197,6 +203,9 @@ public class BridgeSettingsDescriptor { public void setSomfyAddress(IpList somfyAddress) { this.somfyaddress = somfyAddress; } + public void setHomeWizardAddress(IpList homewizardaddress) { + this.homewizardaddress = homewizardaddress; + } public IpList getHarmonyAddress() { return harmonyaddress; } @@ -236,6 +245,9 @@ public class BridgeSettingsDescriptor { public boolean isSomfyconfigured() { return somfyconfigured; } + public boolean isHomeWizardConfigured() { + return homewizardconfigured; + } public void setVeraconfigured(boolean veraconfigured) { this.veraconfigured = veraconfigured; } @@ -245,6 +257,9 @@ public class BridgeSettingsDescriptor { public void setSomfyconfigured(boolean somfyconfigured) { this.somfyconfigured = somfyconfigured; } + public void setHomeWizardConfigured(boolean homewizardconfigured) { + this.homewizardconfigured = homewizardconfigured; + } public boolean isHarmonyconfigured() { return harmonyconfigured; } @@ -492,4 +507,14 @@ public class BridgeSettingsDescriptor { this.setSettingsChanged(true); } } + public Boolean isValidHomeWizard() { + if(this.getHomeWizardAddress() == null || this.getHomeWizardAddress().getDevices().size() <= 0) + return false; + + List devicesList = this.getHomeWizardAddress().getDevices(); + if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS)) + return false; + + return true; + } } diff --git a/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java b/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java index 9c13d71..b32e9d9 100644 --- a/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java +++ b/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java @@ -22,6 +22,7 @@ public class DeviceMapTypes { public final static String[] EXEC_DEVICE_COMPAT = { "exec", "Execute Script/Program"}; public final static String[] CMD_DEVICE = { "cmdDevice", "Execute Command/Script/Program"}; public final static String[] HASS_DEVICE = { "hassDevice", "HomeAssistant Device"}; + public final static String[] HOMEWIZARD_DEVICE = { "homewizardDevice", "HomeWizard Device"}; public final static String[] TCP_DEVICE = { "tcpDevice", "TCP Device"}; public final static String[] TCP_DEVICE_COMPAT = { "TCP", "TCP Device"}; public final static String[] UDP_DEVICE = { "udpDevice", "UDP Device"}; @@ -48,6 +49,7 @@ public class DeviceMapTypes { deviceMapTypes.add(HARMONY_ACTIVITY); deviceMapTypes.add(HARMONY_BUTTON); deviceMapTypes.add(HASS_DEVICE); + deviceMapTypes.add(HOMEWIZARD_DEVICE); deviceMapTypes.add(HTTP_DEVICE); deviceMapTypes.add(HUE_DEVICE); deviceMapTypes.add(LIFX_DEVICE); diff --git a/src/main/java/com/bwssystems/HABridge/HomeManager.java b/src/main/java/com/bwssystems/HABridge/HomeManager.java index 0d6824c..f488885 100644 --- a/src/main/java/com/bwssystems/HABridge/HomeManager.java +++ b/src/main/java/com/bwssystems/HABridge/HomeManager.java @@ -14,6 +14,7 @@ import com.bwssystems.HABridge.plugins.exec.CommandHome; import com.bwssystems.HABridge.plugins.hal.HalHome; import com.bwssystems.HABridge.plugins.harmony.HarmonyHome; import com.bwssystems.HABridge.plugins.hass.HassHome; +import com.bwssystems.HABridge.plugins.homewizard.HomeWizardHome; import com.bwssystems.HABridge.plugins.http.HTTPHome; import com.bwssystems.HABridge.plugins.hue.HueHome; import com.bwssystems.HABridge.plugins.lifx.LifxHome; @@ -68,6 +69,10 @@ public class HomeManager { aHome = new HassHome(bridgeSettings); resourceList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome); homeList.put(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex], aHome); + // Setup the HomeWizard configuration if available + aHome = new HomeWizardHome(bridgeSettings); + resourceList.put(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex], aHome); + homeList.put(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex], aHome); //setup the command execution Home aHome = new CommandHome(bridgeSettings); homeList.put(DeviceMapTypes.EXEC_DEVICE_COMPAT[DeviceMapTypes.typeIndex], aHome); @@ -96,7 +101,7 @@ public class HomeManager { aHome = new FibaroHome(bridgeSettings); resourceList.put(DeviceMapTypes.FIBARO_DEVICE[DeviceMapTypes.typeIndex], aHome); resourceList.put(DeviceMapTypes.FIBARO_SCENE[DeviceMapTypes.typeIndex], aHome); - //setup the Domoticz configuration if available + //setup the Domoticz configuration if available aHome = new DomoticzHome(bridgeSettings); homeList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome); resourceList.put(DeviceMapTypes.DOMOTICZ_DEVICE[DeviceMapTypes.typeIndex], aHome); diff --git a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java index c3067b1..849ec21 100644 --- a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java +++ b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java @@ -291,6 +291,12 @@ public class DeviceResource { return homeManager.findResource(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HASS_DEVICE[DeviceMapTypes.typeIndex]); }, new JsonTransformer()); + get (API_CONTEXT + "/homewizard/devices", "application/json", (request, response) -> { + log.debug("Get HomeWizard Clients"); + response.status(HttpStatus.SC_OK); + return homeManager.findResource(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex]); + }, new JsonTransformer()); + get (API_CONTEXT + "/domoticz/devices", "application/json", (request, response) -> { log.debug("Get Domoticz Clients"); response.status(HttpStatus.SC_OK); diff --git a/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardHome.java b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardHome.java new file mode 100644 index 0000000..02d7278 --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardHome.java @@ -0,0 +1,151 @@ +package com.bwssystems.HABridge.plugins.homewizard; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bwssystems.HABridge.BridgeSettings; +import com.bwssystems.HABridge.DeviceMapTypes; +import com.bwssystems.HABridge.Home; +import com.bwssystems.HABridge.NamedIP; +import com.bwssystems.HABridge.api.CallItem; +import com.bwssystems.HABridge.dao.DeviceDescriptor; +import com.bwssystems.HABridge.hue.ColorData; +import com.bwssystems.HABridge.hue.MultiCommandUtil; + +/** + * Control HomeWizard devices over HomeWizard Cloud + * + * @author Björn Rennfanz (bjoern@fam-rennfanz.de) + * + */ +public class HomeWizardHome implements Home { + + private static final Logger log = LoggerFactory.getLogger(HomeWizardHome.class); + + private Map plugGateways; + private Boolean validHomeWizard; + private boolean closed; + + public HomeWizardHome(BridgeSettings bridgeSettings) { + super(); + closed = true; + createHome(bridgeSettings); + closed = false; + } + + @Override + public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity, + Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) { + + String responseString = null; + if (!validHomeWizard) { + + log.warn("Should not get here, no HomeWizard smart plug available"); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + + "\",\"description\": \"Should not get here, no HomeWizard smart plug available\", \"parameter\": \"/lights/" + + lightId + "state\"}}]"; + } else { + + if (anItem.getType() != null && anItem.getType().trim().equalsIgnoreCase(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex])) { + + log.debug("Executing HUE api request to change activity to HomeWizard smart plug: " + anItem.getItem().toString()); + String jsonToPost = anItem.getItem().toString(); + + HomeWizzardSmartPlugInfo homeWizzardHandler = getHomeWizzardHandler(device.getTargetDevice()); + if(homeWizzardHandler == null) { + log.warn("Should not get here, no HomeWizard smart plug configured"); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + + "\",\"description\": \"Should not get here, no HomeWizard smart plug configured\", \"parameter\": \"/lights/" + + lightId + "state\"}}]"; + } else { + try { + + homeWizzardHandler.execApply(jsonToPost); + } catch (Exception e) { + + log.warn("Error posting request to HomeWizard smart plug"); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + + "\",\"description\": \"Error posting request to HomeWizard smart plug\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; + } + } + } + } + + return responseString; + } + + public HomeWizzardSmartPlugInfo getHomeWizzardHandler(String plugName) { + return plugGateways.get(plugName); + } + + public List getDevices() { + + log.debug("consolidating devices for plug gateways"); + Iterator keys = plugGateways.keySet().iterator(); + ArrayList deviceList = new ArrayList<>(); + + while(keys.hasNext()) + { + String key = keys.next(); + for(HomeWizardSmartPlugDevice device : plugGateways.get(key).getDevices()) + deviceList.add(device); + } + + return deviceList; + } + + @Override + public Object getItems(String type) { + + if (validHomeWizard) + { + if (type.equalsIgnoreCase(DeviceMapTypes.HOMEWIZARD_DEVICE[DeviceMapTypes.typeIndex])) + { + return getDevices(); + } + } + + return null; + } + + @Override + public Home createHome(BridgeSettings bridgeSettings) { + + validHomeWizard = bridgeSettings.getBridgeSettingsDescriptor().isValidHomeWizard(); + log.info("HomeWizard Home created. " + (validHomeWizard ? "" : "No HomeWizard gateways configured.")); + + if (validHomeWizard) + { + plugGateways = new HashMap<>(); + Iterator gatewaysList = bridgeSettings.getBridgeSettingsDescriptor().getHomeWizardAddress().getDevices().iterator(); + + while(gatewaysList.hasNext()) { + + NamedIP gateway = gatewaysList.next(); + plugGateways.put(gateway.getName(), new HomeWizzardSmartPlugInfo(gateway, gateway.getName())); + } + } + + return this; + } + + @Override + public void closeHome() { + + log.debug("Closing Home."); + if(closed) { + + log.debug("Home is already closed...."); + return; + } + + plugGateways = null; + closed = true; + } +} diff --git a/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardSmartPlugDevice.java b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardSmartPlugDevice.java new file mode 100644 index 0000000..83b4b0a --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizardSmartPlugDevice.java @@ -0,0 +1,47 @@ +package com.bwssystems.HABridge.plugins.homewizard; + +/** + * Control HomeWizard devices over HomeWizard Cloud + * + * @author Björn Rennfanz (bjoern@fam-rennfanz.de) + * + */ +public class HomeWizardSmartPlugDevice { + + private String name; + private String gateway; + private String id; + private String typeName; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGateway() { + return gateway; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTypeName() { + return this.typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } +} diff --git a/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizzardSmartPlugInfo.java b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizzardSmartPlugInfo.java new file mode 100644 index 0000000..e27b2a2 --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/HomeWizzardSmartPlugInfo.java @@ -0,0 +1,215 @@ +package com.bwssystems.HABridge.plugins.homewizard; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bwssystems.HABridge.NamedIP; +import com.bwssystems.HABridge.plugins.homewizard.json.Device; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import us.monoid.json.JSONException; +import us.monoid.json.JSONObject; + +/** + * Control HomeWizard devices over HomeWizard Cloud + * + * @author Björn Rennfanz (bjoern@fam-rennfanz.de) + * + */ +public class HomeWizzardSmartPlugInfo { + + private static final Logger log = LoggerFactory.getLogger(HomeWizardHome.class); + + private static final String HOMEWIZARD_LOGIN_URL = "https://cloud.homewizard.com/account/login"; + private static final String HOMEWIZARD_API_URL = "https://plug.homewizard.com/plugs"; + private static final String EMPTY_STRING = ""; + + private final String cloudAuth; + private final Gson gson; + + private String cloudSessionId; + private String cloudPlugName; + private String cloudPlugId; + + public HomeWizzardSmartPlugInfo(NamedIP gateway, String name) { + + super(); + + cloudAuth = "Basic " + new String(Base64.encodeBase64((gateway.getUsername() + ":" + DigestUtils.sha1Hex(gateway.getPassword())).getBytes())); + cloudPlugName = name; + gson = new Gson(); + } + + public boolean login() + { + try + { + URL url = new URL(HOMEWIZARD_LOGIN_URL); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("Authorization", cloudAuth); + connection.setRequestProperty("Content-Type", "application/json;charset=utf-8"); + connection.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder buffer = new StringBuilder(); + String line; + + while((line = br.readLine()) != null) + { + buffer.append(line).append("\n"); + } + br.close(); + + // Get session id from result JSON + JSONObject json = new JSONObject(buffer.toString()); + cloudSessionId = json.get("session").toString(); + } + catch(IOException | JSONException e) + { + log.warn("Error while login to cloud service ", e); + return false; + } + + return true; + } + + private String requestJson(String request) + { + String result = null; + + // Check login was successful + if (login()) { + + // Request JSON from Cloud service + try + { + URL url = new URL(HOMEWIZARD_API_URL + request); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + connection.setRequestMethod("GET"); + connection.setRequestProperty("X-Session-Token", cloudSessionId); + connection.setRequestProperty("Content-Type", "application/json;charset=utf-8"); + connection.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuilder buffer = new StringBuilder(); + String line; + + while((line = br.readLine()) != null) + { + buffer.append(line).append("\n"); + } + + br.close(); + + result = buffer.toString(); + result = StringUtils.strip(result, "[]"); + } + catch(IOException e) + { + log.warn("Error while get json request: {} ", request, e); + } + } + + return result; + } + + private boolean sendAction(String request, String action) + { + // Check login was successful + if (login()) { + + // Post action into Cloud service + try + { + URL url = new URL(HOMEWIZARD_API_URL + request); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("X-Session-Token", cloudSessionId); + connection.setRequestProperty("Content-Type", "application/json;charset=utf-8"); + + JsonObject actionJson = new JsonObject(); + actionJson.addProperty("action", action); + + OutputStream os = connection.getOutputStream(); + os.write(actionJson.toString().getBytes("UTF-8")); + os.close(); + } + catch(IOException e) + { + log.warn("Error while post json action: {} ", request, e); + return false; + } + } + else + { + return false; + } + + return true; + } + + public List getDevices() + { + List homewizardDevices = new ArrayList<>(); + try { + + String result = requestJson(EMPTY_STRING); + JSONObject resultJson = new JSONObject(result); + cloudPlugId = resultJson.getString("id"); + + String all_devices_json = resultJson.get("devices").toString(); + Device[] devices = gson.fromJson(all_devices_json, Device[].class); + + // Fix names from JSON + for (Device device : devices) { + device.setTypeName(StringUtils.capitalize(device.getTypeName().replace("_", " "))); + homewizardDevices.add(mapDeviceToHomeWizardSmartPlugDevice(device)); + } + } + catch(JSONException e) { + log.warn("Error while get devices from cloud service ", e); + } + + log.info("Found: " + homewizardDevices.size() + " devices"); + return homewizardDevices; + } + + public void execApply(String jsonToPost) { + try + { + JSONObject resultJson = new JSONObject(jsonToPost); + String deviceId = resultJson.getString("deviceid"); + String action = resultJson.getString("action"); + + sendAction("/" + cloudPlugId + "/devices/" + deviceId + "/action", action); + } + catch(JSONException e) { + log.warn("Error while get devices from cloud service ", e); + } + } + + protected HomeWizardSmartPlugDevice mapDeviceToHomeWizardSmartPlugDevice(Device device) { + HomeWizardSmartPlugDevice homewizardDevice = new HomeWizardSmartPlugDevice(); + homewizardDevice.setId(device.getId()); + homewizardDevice.setGateway(cloudPlugName); + homewizardDevice.setName(device.getName()); + homewizardDevice.setTypeName(device.getTypeName()); + + return homewizardDevice; + } +} diff --git a/src/main/java/com/bwssystems/HABridge/plugins/homewizard/json/Device.java b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/json/Device.java new file mode 100644 index 0000000..01052e7 --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/plugins/homewizard/json/Device.java @@ -0,0 +1,49 @@ +package com.bwssystems.HABridge.plugins.homewizard.json; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Control HomeWizard devices over HomeWizard Cloud + * + * @author Björn Rennfanz (bjoern@fam-rennfanz.de) + * + */ +public class Device { + + @SerializedName("id") + @Expose + private String id; + + @SerializedName("name") + @Expose + private String name; + + @SerializedName("typeName") + @Expose + private String typeName; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTypeName() { + return this.typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } +}