diff --git a/src/main/java/com/bwssystems/HABridge/HABridge.java b/src/main/java/com/bwssystems/HABridge/HABridge.java index 7747cb2..1ae6daf 100644 --- a/src/main/java/com/bwssystems/HABridge/HABridge.java +++ b/src/main/java/com/bwssystems/HABridge/HABridge.java @@ -69,7 +69,7 @@ public class HABridge { // setup the class to handle the resource setup rest api theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome); // setup the class to handle the hue emulator rest api - theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome); + theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome); theHueMulator.setupServer(); // setup the class to handle the upnp response rest api theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor()); diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 7722e63..2634e09 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -13,6 +13,8 @@ import com.bwssystems.harmony.HarmonyHandler; import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.harmony.RunActivity; import com.bwssystems.hue.HueDeviceIdentifier; +import com.bwssystems.hue.HueErrorStringSet; +import com.bwssystems.hue.HueHome; import com.bwssystems.hue.HueUtil; import com.bwssystems.nest.controller.Nest; import com.bwssystems.util.JsonTransformer; @@ -47,6 +49,7 @@ import java.math.BigDecimal; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -57,7 +60,7 @@ import javax.xml.bind.DatatypeConverter; * Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server */ -public class HueMulator { +public class HueMulator implements HueErrorStringSet { private static final Logger log = LoggerFactory.getLogger(HueMulator.class); private static final String INTENSITY_PERCENT = "${intensity.percent}"; private static final String INTENSITY_BYTE = "${intensity.byte}"; @@ -69,14 +72,16 @@ public class HueMulator { private DeviceRepository repository; private HarmonyHome myHarmonyHome; private Nest theNest; + private HueHome myHueHome; private HttpClient httpClient; private ObjectMapper mapper; private BridgeSettingsDescriptor bridgeSettings; private byte[] sendData; private String hueUser; + private String errorString; - public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome){ + public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome){ httpClient = HttpClients.createDefault(); mapper = new ObjectMapper(); //armzilla: work around Echo incorrect content type and breaking mapping. Map manually mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @@ -89,13 +94,17 @@ public class HueMulator { this.theNest = aNestHome.getTheNest(); else this.theNest = null; + if(theBridgeSettings.isValidHue()) + this.myHueHome = aHueHome; + else + this.myHueHome = null; bridgeSettings = theBridgeSettings; hueUser = null; + errorString = null; } // This function sets up the sparkjava rest calls for the hue api public void setupServer() { - String errorString = null; log.info("Hue emulator service started...."); // http://ip_address:port/api/{userId}/lights returns json objects of all lights configured get(HUE_CONTEXT + "/:userid/lights", "application/json", (request, response) -> { @@ -345,23 +354,38 @@ public class HueMulator { if(device.getDeviceType().toLowerCase().contains("hue") || (device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice"))) { - url = device.getOnUrl(); - HueDeviceIdentifier deviceId = new Gson().fromJson(url, HueDeviceIdentifier.class); - if(hueUser == null) { - hueUser = userId; - if((hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), errorString)) == null) { - return errorString; - } + if(myHueHome != null) { + url = device.getOnUrl(); + HueDeviceIdentifier deviceId = new Gson().fromJson(url, HueDeviceIdentifier.class); + if(myHueHome.getTheHUERegisteredUser() == null) { + hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this); + if(hueUser == null) { + return errorString; + } + myHueHome.setTheHUERegisteredUser(hueUser); + } + + // make call + responseString = doHttpRequest("http://"+deviceId.getIpAddress()+"/api/"+myHueHome.getTheHUERegisteredUser()+"/lights/"+deviceId.getDeviceId()+"/state", HttpPut.METHOD_NAME, device.getContentType(), request.body()); + if (responseString == null) { + log.warn("Error on calling url to change device state: " + url); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling HUE to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; + } + else if(responseString.contains("[{\"error\":") && responseString.contains("unauthorized user")) { + myHueHome.setTheHUERegisteredUser(null); + hueUser = HueUtil.registerWithHue(httpClient, deviceId.getIpAddress(), device.getName(), myHueHome.getTheHUERegisteredUser(), this); + if(hueUser == null) { + return errorString; + } + myHueHome.setTheHUERegisteredUser(hueUser); + } + else if(!responseString.contains("[{\"error\":")) + device.setDeviceState(state); } - - // make call - if (!doHttpRequest("http://"+deviceId.getIpAddress()+"/api/"+hueUser+"/lights/"+deviceId.getDeviceId()+"/state", HttpPut.METHOD_NAME, device.getContentType(), request.body())) { - log.warn("Error on calling url to change device state: " + url); - responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; - } - else - device.setDeviceState(state); - return responseString; + else + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"No HUE configured\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; + + return responseString; } if(request.body().contains("bri")) @@ -520,7 +544,7 @@ public class HueMulator { else body = replaceIntensityValue(device.getContentBodyOff(), state.getBri()); // make call - if (!doHttpRequest(url, device.getHttpVerb(), device.getContentType(), body)) { + if (doHttpRequest(url, device.getHttpVerb(), device.getContentType(), body) == null) { log.warn("Error on calling url to change device state: " + url); responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling url to change device state\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; } @@ -572,8 +596,9 @@ public class HueMulator { // This function executes the url from the device repository against the vera - protected boolean doHttpRequest(String url, String httpVerb, String contentType, String body) { + protected String doHttpRequest(String url, String httpVerb, String contentType, String body) { HttpUriRequest request = null; + String theContent = null; try { if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) { request = new HttpGet(url); @@ -592,20 +617,20 @@ public class HueMulator { } } catch(IllegalArgumentException e) { log.warn("Error calling out to HA gateway: IllegalArgumentException in log", e); - return false; + return null; } log.debug("Making outbound call in doHttpRequest: " + request); try { HttpResponse response = httpClient.execute(request); - EntityUtils.consume(response.getEntity()); //close out inputstream ignore content log.debug((httpVerb == null?"GET":httpVerb) + " execute on URL responded: " + response.getStatusLine().getStatusCode()); if(response.getStatusLine().getStatusCode() >= 200 && response.getStatusLine().getStatusCode() < 300){ - return true; + theContent = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8")); //read content for data + EntityUtils.consume(response.getEntity()); //close out inputstream ignore content } } catch (IOException e) { log.warn("Error calling out to HA gateway: IOException in log", e); } - return false; + return theContent; } private String formatSuccessHueResponse(DeviceState state, String body, String lightId) { @@ -676,4 +701,9 @@ public class HueMulator { return responseString; } + + @Override + public void setErrorString(String anError) { + errorString = anError; + } } diff --git a/src/main/java/com/bwssystems/hue/HueDevice.java b/src/main/java/com/bwssystems/hue/HueDevice.java index 6389709..1358d5a 100644 --- a/src/main/java/com/bwssystems/hue/HueDevice.java +++ b/src/main/java/com/bwssystems/hue/HueDevice.java @@ -5,6 +5,7 @@ import com.bwssystems.HABridge.api.hue.DeviceResponse; public class HueDevice { private DeviceResponse device; + private String huedeviceid; private String hueaddress; private String huename; public DeviceResponse getDevice() { @@ -13,6 +14,12 @@ public class HueDevice { public void setDevice(DeviceResponse adevice) { this.device = adevice; } + public String getHuedeviceid() { + return huedeviceid; + } + public void setHuedeviceid(String huedeviceid) { + this.huedeviceid = huedeviceid; + } public String getHueaddress() { return hueaddress; } diff --git a/src/main/java/com/bwssystems/hue/HueErrorStringSet.java b/src/main/java/com/bwssystems/hue/HueErrorStringSet.java new file mode 100644 index 0000000..9cbaae1 --- /dev/null +++ b/src/main/java/com/bwssystems/hue/HueErrorStringSet.java @@ -0,0 +1,5 @@ +package com.bwssystems.hue; + +public interface HueErrorStringSet { + public void setErrorString(String anError); +} diff --git a/src/main/java/com/bwssystems/hue/HueHome.java b/src/main/java/com/bwssystems/hue/HueHome.java index c7200b9..32af368 100644 --- a/src/main/java/com/bwssystems/hue/HueHome.java +++ b/src/main/java/com/bwssystems/hue/HueHome.java @@ -17,6 +17,7 @@ import com.bwssystems.HABridge.api.hue.HueApiResponse; public class HueHome { private static final Logger log = LoggerFactory.getLogger(HueHome.class); private Map hues; + private String theHUERegisteredUser; public HueHome(BridgeSettingsDescriptor bridgeSettings) { hues = new HashMap(); @@ -25,8 +26,9 @@ public class HueHome { Iterator theList = bridgeSettings.getHueaddress().getDevices().iterator(); while(theList.hasNext()) { NamedIP aHue = theList.next(); - hues.put(aHue.getName(), new HueInfo(aHue)); + hues.put(aHue.getName(), new HueInfo(aHue, this)); } + theHUERegisteredUser = null; } public List getDevices() { @@ -41,8 +43,10 @@ public class HueHome { if(theDevices != null) { Iterator deviceKeys = theDevices.keySet().iterator(); while(deviceKeys.hasNext()) { + String theDeviceKey = deviceKeys.next(); HueDevice aNewHueDevice = new HueDevice(); - aNewHueDevice.setDevice(theDevices.get(deviceKeys.next())); + aNewHueDevice.setDevice(theDevices.get(theDeviceKey)); + aNewHueDevice.setHuedeviceid(theDeviceKey); aNewHueDevice.setHueaddress(hues.get(key).getHueAddress().getIp()); aNewHueDevice.setHuename(key); deviceList.add(aNewHueDevice); @@ -58,4 +62,12 @@ public class HueHome { } return deviceList; } + + public String getTheHUERegisteredUser() { + return theHUERegisteredUser; + } + + public void setTheHUERegisteredUser(String theHUERegisteredUser) { + this.theHUERegisteredUser = theHUERegisteredUser; + } } diff --git a/src/main/java/com/bwssystems/hue/HueInfo.java b/src/main/java/com/bwssystems/hue/HueInfo.java index f3cb48c..7b9a265 100644 --- a/src/main/java/com/bwssystems/hue/HueInfo.java +++ b/src/main/java/com/bwssystems/hue/HueInfo.java @@ -15,22 +15,24 @@ import com.bwssystems.HABridge.api.hue.HueApiResponse; import com.google.gson.Gson; -public class HueInfo { +public class HueInfo implements HueErrorStringSet { private static final Logger log = LoggerFactory.getLogger(HueInfo.class); private HttpClient httpClient; private NamedIP hueAddress; private String theUser; + private HueHome theHueHome; + private String errorString = null; - public HueInfo(NamedIP addressName) { + public HueInfo(NamedIP addressName, HueHome aHueHome) { super(); httpClient = HttpClients.createDefault(); hueAddress = addressName; theUser = "habridge"; + theHueHome = aHueHome; } public HueApiResponse getHueApiResponse() { HueApiResponse theHueApiResponse = null; - String errorString = null; String theUrl = "http://" + hueAddress.getIp() + HueUtil.HUE_REQUEST + "/" + theUser; String theData; @@ -48,10 +50,13 @@ public class HueInfo { log.debug("GET HueApiResponse - data: " + theData); if(theData.contains("[{\"error\":")) { if(theData.contains("unauthorized user")) { - if((theUser = HueUtil.registerWithHue(httpClient, hueAddress.getIp(), hueAddress.getName(), errorString)) == null) { + theUser = HueUtil.registerWithHue(httpClient, hueAddress.getIp(), hueAddress.getName(), theHueHome.getTheHUERegisteredUser(), this); + if(theUser == null) { log.warn("Register to Hue for " + hueAddress.getName() + " returned error: " + errorString); return null; } + else + theHueHome.setTheHUERegisteredUser(theUser); retryCount++; } else { @@ -98,4 +103,9 @@ public class HueInfo { public void setHueAddress(NamedIP hueAddress) { this.hueAddress = hueAddress; } + + @Override + public void setErrorString(String anError) { + errorString = anError; + } } diff --git a/src/main/java/com/bwssystems/hue/HueUtil.java b/src/main/java/com/bwssystems/hue/HueUtil.java index 105cc51..4c955d2 100644 --- a/src/main/java/com/bwssystems/hue/HueUtil.java +++ b/src/main/java/com/bwssystems/hue/HueUtil.java @@ -19,12 +19,13 @@ public class HueUtil { private static final Logger log = LoggerFactory.getLogger(HueUtil.class); public static final String HUE_REQUEST = "/api"; - public static final String registerWithHue(HttpClient anHttpClient, String ipAddress, String aName, String errorString) { - UserCreateRequest theLogin; - String theUser = null; - theLogin = new UserCreateRequest(); + public static final String registerWithHue(HttpClient anHttpClient, String ipAddress, String aName, String theUser, HueErrorStringSet errorStringSet) { + UserCreateRequest theLogin = new UserCreateRequest(); theLogin.setDevicetype("HA Bridge"); - theLogin.setUsername("habridge"); + if(theUser == null) + theLogin.setUsername("habridge"); + else + theLogin.setUsername(theUser); HttpPost postRequest = new HttpPost("http://" + ipAddress + HUE_REQUEST); ContentType parsedContentType = ContentType.parse("application/json"); StringEntity requestBody = new StringEntity(new Gson().toJson(theLogin), parsedContentType); @@ -42,7 +43,7 @@ public class HueUtil { } else log.warn("registerWithHue returned an unexpected error: " + theBody); - errorString = theBody; + errorStringSet.setErrorString(theBody); } else { SuccessUserResponse[] theResponses = new Gson().fromJson(theBody, SuccessUserResponse[].class); //read content for data, SuccessUserResponse[].class);