From c20d046b3064e10745d95d077dea9aceb196d313 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 15 Sep 2016 15:41:59 -0500 Subject: [PATCH 01/12] Updated upnp response to be closer to the hue bridge v2 spec. Updated handling for is on when bri is 0. Updated exec to check for empty call. --- .../bwssystems/HABridge/hue/HueMulator.java | 51 +++++++++++++------ .../HABridge/upnp/UpnpListener.java | 21 ++++++-- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 27f941c..416ef68 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -490,8 +490,12 @@ public class HueMulator implements HueErrorStringSet { return responseString; } - if (request.body().contains("\"bri\"")) - stateHasBri = true; + if (request.body().contains("\"bri\"")) { + if(theStateChanges.isOn() && theStateChanges.getBri() == 0) + stateHasBri = false; + else + stateHasBri = true; + } if (request.body().contains("\"bri_inc\"")) stateHasBriInc = true; @@ -576,8 +580,12 @@ public class HueMulator implements HueErrorStringSet { return responseString; } - if (request.body().contains("\"bri\"")) - stateHasBri = true; + if (request.body().contains("\"bri\"")) { + if(theStateChanges.isOn() && theStateChanges.getBri() == 0) + stateHasBri = false; + else + stateHasBri = true; + } if (request.body().contains("\"bri_inc\"")) stateHasBriInc = true; @@ -1025,19 +1033,30 @@ public class HueMulator implements HueErrorStringSet { return theContent; } - private String doExecRequest(String anItem, int intensity, String lightId) { + private String doExecRequest(String anItem, int intensity, String lightId) { log.debug("Executing request: " + anItem); - String responseString = null; - try { - Process p = Runtime.getRuntime().exec(replaceIntensityValue(anItem, intensity, false)); - log.debug("Process running: " + p.isAlive()); - } catch (IOException e) { - log.warn("Could not execute request: " + anItem, e); - responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + "state\"}}]"; - } - return responseString; - } - + String responseString = null; + if(anItem != null && !anItem.equalsIgnoreCase("")) { + try { + Process p = Runtime.getRuntime().exec(replaceIntensityValue(anItem, intensity, false)); + log.debug("Process running: " + p.isAlive()); + } catch (IOException e) { + log.warn("Could not execute request: " + anItem, e); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + + "state\"}}]"; + } + } + else { + log.warn("Could not execute request. Request is empty."); + responseString = "[{\"error\":{\"type\": 6, \"address\": \"/lights/" + lightId + + "\",\"description\": \"Error on calling out to device\", \"parameter\": \"/lights/" + lightId + + "state\"}}]"; + } + + return responseString; + } + private String formatSuccessHueResponse(StateChangeBody state, String body, String lightId, DeviceState deviceState) { String responseString = "["; diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index 0d4c8d2..14774c9 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.BridgeControlDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.Configuration; +import com.bwssystems.HABridge.api.hue.HuePublicConfig; import java.io.IOException; import java.net.*; @@ -24,6 +25,15 @@ public class UpnpListener { private BridgeControlDescriptor bridgeControl; private boolean discoveryTemplateLatest; private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + + "HOST: %s:%s" + + "CACHE-CONTROL: max-age=86400\r\n" + + "EXT:\r\n" + + "LOCATION: http://%s:%s/description.xml\r\n" + + "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + + "hue-bridgeid: %s\r\n" + + "ST: upnp:rootdevice\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-001788102201::upnp:rootdevice\r\n\r\n"; + private String discoveryTemplate091516 = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + @@ -33,10 +43,11 @@ public class UpnpListener { private String discoveryTemplateOld = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n" + - "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n" + + "LOCATION: http://%s:%s/upnp/amazon-ha-bridge/setup.xml\r\n" + + "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + + "01-NLS: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + - "USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n"; + "USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n"; public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl) { super(); @@ -208,9 +219,9 @@ public class UpnpListener { protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException { String discoveryResponse = null; if(discoveryTemplateLatest) - discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort); + discoveryResponse = String.format(discoveryTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, HuePublicConfig.createConfig("temp", responseAddress).getBridgeid()); else - discoveryResponse = String.format(discoveryTemplateOld, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort); + discoveryResponse = String.format(discoveryTemplate091516, responseAddress, httpServerPort); if(traceupnp) log.info("Traceupnp: sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort); else From ad4015927c2d6e620a87e108278447df1f7fed4e Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 21 Sep 2016 16:33:57 -0500 Subject: [PATCH 02/12] Add check for host is down exception on upnp send in listener and not shutdown only a warning. --- .../java/com/bwssystems/HABridge/upnp/UpnpListener.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index 14774c9..d99157d 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -148,8 +148,12 @@ public class UpnpListener { try { sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort()); } catch (IOException e) { - log.error("UpnpListener encountered an error sending upnp response packet. Shutting down", e); - error = true; + if(e.getMessage().equalsIgnoreCase("Host is down")) + log.warn("UpnpListener encountered an error sending upnp response packet as requesting host is now not available. IP: " + packet.getAddress().getHostAddress()); + else { + log.error("UpnpListener encountered an error sending upnp response packet. Shutting down", e); + error = true; + } } } } catch (IOException e) { From faa67827c636065def014d4e877c42879f378eda Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 23 Sep 2016 15:46:39 -0500 Subject: [PATCH 03/12] Updated configuration items for hue responses. ADded call thru for hue devices configured for state info. added dim content body. --- pom.xml | 4 +- .../HABridge/api/hue/HueConfig.java | 4 +- .../HABridge/api/hue/HuePublicConfig.java | 4 +- .../HABridge/dao/DeviceDescriptor.java | 11 +++ .../bwssystems/HABridge/hue/HueMulator.java | 72 ++++++++++++++++++- src/main/resources/public/scripts/app.js | 4 ++ .../resources/public/views/editdevice.html | 13 ++++ src/main/resources/public/views/editor.html | 13 ++++ 8 files changed, 117 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index f28f523..29fddc2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0 + 3.1.0a jar HA Bridge @@ -43,7 +43,7 @@ com.github.bwssytems nest-controller - 1.0.8 + 1.0.9 org.slf4j diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java index 4562a26..2cec13b 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java @@ -40,10 +40,10 @@ public class HueConfig SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC")); aConfig.setMac(HueConfig.getMacAddress(ipaddress)); - aConfig.setApiversion("1.10.0"); + aConfig.setApiversion("1.14.0"); aConfig.setPortalservices(false); aConfig.setGateway(ipaddress); - aConfig.setSwversion("01028090"); + aConfig.setSwversion("01033989"); aConfig.setLinkbutton(false); aConfig.setIpaddress(ipaddress); aConfig.setProxyport(0); diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java index c927446..b731d92 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java @@ -23,8 +23,8 @@ public class HuePublicConfig public static HuePublicConfig createConfig(String name, String ipaddress) { HuePublicConfig aConfig = new HuePublicConfig(); aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress)); - aConfig.setApiversion("1.10.0"); - aConfig.setSwversion("01028090"); + aConfig.setApiversion("1.14.0"); + aConfig.setSwversion("01033989"); aConfig.setName(name); aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress)); aConfig.setModelid("BSB002"); diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java index 2090b7c..6ed7b95 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java @@ -50,6 +50,9 @@ public class DeviceDescriptor{ @SerializedName("contentBodyOff") @Expose private String contentBodyOff; + @SerializedName("contentBodyDim") + @Expose + private String contentBodyDim; private DeviceState deviceState; @@ -165,6 +168,14 @@ public class DeviceDescriptor{ this.contentBodyOff = contentBodyOff; } + public String getContentBodyDim() { + return contentBodyDim; + } + + public void setContentBodyDim(String contentBodyDim) { + this.contentBodyDim = contentBodyDim; + } + public DeviceState getDeviceState() { if(deviceState == null) deviceState = DeviceState.createDeviceState(); diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 416ef68..b99303e 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -280,7 +280,41 @@ public class HueMulator implements HueErrorStringSet { List deviceList = repository.findAll(); Map deviceResponseMap = new HashMap<>(); for (DeviceDescriptor device : deviceList) { - DeviceResponse deviceResponse = DeviceResponse.createResponse(device); + DeviceResponse deviceResponse = null; + String responseString; + if((device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice"))) { + HueDeviceIdentifier deviceId = new Gson().fromJson(device.getOnUrl(), 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(), HttpGet.METHOD_NAME, device.getContentType(), null, null); + if (responseString == null) { + log.warn("Error on calling hue device to get state: " + device.getName()); + deviceResponse = DeviceResponse.createResponse(device); + } + 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); + deviceResponse = DeviceResponse.createResponse(device); + } + else { + deviceResponse = new Gson().fromJson(responseString, DeviceResponse.class); + if(deviceResponse == null) + deviceResponse = DeviceResponse.createResponse(device); + } + + } + else + deviceResponse = DeviceResponse.createResponse(device); deviceResponseMap.put(device.getId(), deviceResponse); } return deviceResponseMap; @@ -448,7 +482,41 @@ public class HueMulator implements HueErrorStringSet { } else { log.debug("found device named: " + device.getName()); } - DeviceResponse lightResponse = DeviceResponse.createResponse(device); + DeviceResponse lightResponse = null; + String responseString; + if((device.getMapType() != null && device.getMapType().equalsIgnoreCase("hueDevice"))) { + HueDeviceIdentifier deviceId = new Gson().fromJson(device.getOnUrl(), 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(), HttpGet.METHOD_NAME, device.getContentType(), null, null); + if (responseString == null) { + log.warn("Error on calling hue device to get state: " + device.getName()); + lightResponse = DeviceResponse.createResponse(device); + } + 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); + lightResponse = DeviceResponse.createResponse(device); + } + else { + lightResponse = new Gson().fromJson(responseString, DeviceResponse.class); + if(lightResponse == null) + lightResponse = DeviceResponse.createResponse(device); + } + + } + else + lightResponse = DeviceResponse.createResponse(device); return lightResponse; }, new JsonTransformer()); diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 6fe822b..b6c2cf1 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -131,6 +131,7 @@ app.service('bridgeService', function ($http, $window, ngToast) { self.state.device.httpVerb = null; self.state.device.contentType = null; self.state.device.contentBody = null; + self.state.device.contentBodyDim = null; self.state.device.contentBodyOff = null; self.state.olddevicename = ""; }; @@ -976,6 +977,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi httpVerb: $scope.device.httpVerb, contentType: $scope.device.contentType, contentBody: $scope.device.contentBody, + contentBodyDim: $scope.device.contentBodyDim, contentBodyOff: $scope.device.contentBodyOff }; } @@ -1309,6 +1311,7 @@ app.controller('HueController', function ($scope, $location, $http, bridgeServic httpVerb: $scope.device.httpVerb, contentType: $scope.device.contentType, contentBody: $scope.device.contentBody, + contentBodyDim: $scope.device.contentBodyDim, contentBodyOff: $scope.device.contentBodyOff }; } @@ -1627,6 +1630,7 @@ app.controller('HalController', function ($scope, $location, $http, bridgeServic httpVerb: $scope.device.httpVerb, contentType: $scope.device.contentType, contentBody: $scope.device.contentBody, + contentBodyDim: $scope.device.contentBodyDim, contentBodyOff: $scope.device.contentBodyOff }; } diff --git a/src/main/resources/public/views/editdevice.html b/src/main/resources/public/views/editdevice.html index 0d42a26..d11e6ca 100644 --- a/src/main/resources/public/views/editdevice.html +++ b/src/main/resources/public/views/editdevice.html @@ -218,6 +218,19 @@
+
+
+ + +
+ +
+
+
+
+
+
+ + +
+ +
+
+
+
+
+

+ +

+
diff --git a/src/main/resources/public/views/editdevice.html b/src/main/resources/public/views/editdevice.html index d11e6ca..09339bc 100644 --- a/src/main/resources/public/views/editdevice.html +++ b/src/main/resources/public/views/editdevice.html @@ -107,6 +107,14 @@ Clear Device +
+ + +
+ +
+
From 7f7e96465b0434f367b1139c28543e83f83a431f Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 29 Sep 2016 09:25:47 -0500 Subject: [PATCH 06/12] Switched default web port to be 80. This should resolve some issues with first time configs and external apps. Changed upnp listener response to previous style. Set default for link button presses status to true. --- README.md | 42 +++++++++---------- pom.xml | 2 +- .../bwssystems/HABridge/Configuration.java | 2 +- .../HABridge/api/hue/HueConfig.java | 2 +- .../HABridge/upnp/UpnpListener.java | 9 ++++ 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c4d6b12..71db239 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Then locate the jar and start the server with: ATTENTION: This requires JDK 1.8 to run ``` -java -jar ha-bridge-3.1.0.jar +java -jar ha-bridge-3.2.0.jar ``` ### Automation on Linux systems To have this configured and running automatically there are a few resources to use. One is using Docker and a docker container has been built for this and can be gotten here: https://github.com/aptalca/docker-ha-bridge @@ -38,7 +38,7 @@ After=network.target [Service] Type=simple -ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-3.1.0.jar +ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/amazon-echo/data/habridge.config /home/pi/amazon-echo/ha-bridge-3.2.0.jar [Install] WantedBy=multi-user.target @@ -46,11 +46,11 @@ WantedBy=multi-user.target Basic script setup to run the bridge on a pi. -Create the directory and make sure that ha-bridge-3.1.0.jar is in your /home/pi/habridge directory. +Create the directory and make sure that ha-bridge-3.2.0.jar is in your /home/pi/habridge directory. ``` pi@raspberrypi:~ $ mkdir habridge pi@raspberrypi:~ $ cd habridge -pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v3.1.0/ha-bridge-3.1.0.jar +pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v3.2.0/ha-bridge-3.2.0.jar ``` Edit the shell script for starting: ``` @@ -60,7 +60,7 @@ Then cut and past this, modify any locations that are not correct ``` cd /home/pi/habridge rm /home/pi/habridge/habridge-log.txt -nohup java -jar /home/pi/habridge/ha-bridge-3.1.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 & +nohup java -jar /home/pi/habridge/ha-bridge-3.2.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 & chmod 777 /home/pi/habridge/habridge-log.txt ``` Exit and save the file with ctrl-X and follow the prompts and then execute on the command line: @@ -83,14 +83,14 @@ The default location for the configuration file to contain the settings for the java -jar -Dconfig.file=/home/me/data/myhabridge.config ha-bridge-W.X.Y.jar ``` ### -Dserver.port=`` -The default port number for the bridge is 8080. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 8080. The command line example: +The default port number for the bridge is 80. To override what the default or what is in the configuration file for this parameter, specify -Dserver.port=`` explicitly. This is especially helpful if you are running the ha-bridge for the first time and have another application on port 80. The command line example: ``` java -jar -Dserver.port=80 ha-bridge-W.X.Y.jar ``` ## HA Bridge Usage and Configuration This section will cover the basics of configuration and where this configuration can be done. This requires that you have started your bridge process and then have pointed your -favorite web interface by going to the http://: or http://localhost: with port you have assigned. The default quick link is http://localhost:8080 for yoru reference. +favorite web interface by going to the http://: or http://localhost: with port you have assigned. The default quick link is http://localhost for yoru reference. ### The Bridge Devices Tab This screen allows you to see your devices you have configured for the ha-bridge to present to a controller, such as an Amazon Echo/Dot. It gives you a count of devices as there have been reports that the Echo only supports a limited number, but has been growing as of late, YMMV. You can test each device from this page as this calls the ha-bridge just as a controller would, i.e. the Echo. This is useful to make sure your configuration for each device is correct and for trouble shooting. You can also manages your devices as well by editing and making a new device copy as well as deleting it. @@ -108,7 +108,7 @@ The default location for the db to contain the devices as they are added is "dat #### UPNP IP Address The server defaults to the first available address on the host if this is not given. This default may NOT be the correct IP that is your public IP for your host on the network. It is best to set this parameter to not have discovery issues. Replace this value with the server ipv4 address you would like to use as the address that any upnp device will call after discovery. #### Web Server Port -The server defaults to running on port 8080. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should set this port to 80. +The server defaults to running on port 80. To override what the default is, specify a different number. ATTENTION: If you want to use any of the apps made for the Hue to control this bridge, you should keep this port set to 80. #### UPNP Response Port The upnp response port that will be used. The default is 50000. #### Vera Names and IP Addresses @@ -282,7 +282,7 @@ These calls can be accomplished with a REST tool using the following URLs and HT ### Add a device Add a new device to the HA Bridge configuration. There is a basic examples and then three alternate examples for the add. Please note that dimming is supported as well as custom value based on the dimming number given from the echo. This is under the Dimming and Value example. ``` -POST http://host:8080/api/devices +POST http://host/api/devices ``` #### Body Arguments Name | Type | Description | Required @@ -938,16 +938,16 @@ If this criteria is met, the following response is provided to the calling appli HTTP/1.1 200 OK\r\n CACHE-CONTROL: max-age=86400\r\n EXT:\r\n -LOCATION: http://192.168.1.1:8080/description.xml\r\n -SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/0.1\r\n +LOCATION: http://192.168.1.1:80/description.xml\r\n +SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n ST: urn:schemas-upnp-org:device:basic:1\r\n -"USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n +"USN: uuid:2f402f80-da50-11e1-9b23-001788102201::urn:schemas-upnp-org:device:basic:1\r\n\r\n ``` ### UPNP description service The bridge provides the description service which is used by the calling app to interogate access details after it has decided the upnp multicast response is the correct device. #### Get Description ``` -GET http://host:8080/description.xml +GET http://host:80/description.xml ``` #### Response ``` @@ -957,18 +957,18 @@ GET http://host:8080/description.xml 1\n 0\n \n - http://192.168.1.1:8080/\n + http://192.168.1.1:80/\n \n urn:schemas-upnp-org:device:Basic:1\n - HA-Bridge (192.168.1.1)\n + Philips hue (192.168.1.1)\n Royal Philips Electronics\n - http://www.bwssystems.com\n - Hue Emulator for HA bridge\n - Philips hue bridge 2012\n - 929000226503\n - http://www.bwssystems.com/apps.html\n + http://www.philips.com\n + Philips hue Personal Wireless Lighting\n" + Philips hue bridge 2015\n + BSB002\n + http://www.meethue.com\n 0017880ae670\n - uuid:88f6698f-2c83-4393-bd03-cd54a9f8595\n + uuid:2f402f80-da50-11e1-9b23-001788102201\n \n \n (null)\n diff --git a/pom.xml b/pom.xml index 17c4999..3674447 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0c + 3.1.0d jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/Configuration.java b/src/main/java/com/bwssystems/HABridge/Configuration.java index c7673fa..395b63d 100644 --- a/src/main/java/com/bwssystems/HABridge/Configuration.java +++ b/src/main/java/com/bwssystems/HABridge/Configuration.java @@ -6,7 +6,7 @@ public class Configuration { public final static String DEFAULT_ADDRESS = "1.1.1.1"; public final static String LOOP_BACK_ADDRESS = "127.0.0.1"; public final static String LOOP_BACK_INTERFACE = "lo"; - public final static String DEFAULT_WEB_PORT = "8080"; + public final static String DEFAULT_WEB_PORT = "80"; public final static String DEFAULT_BUTTON_SLEEP = "100"; public static final int UPNP_DISCOVERY_PORT = 1900; public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250"; diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java index 2cec13b..705e1c5 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java @@ -44,7 +44,7 @@ public class HueConfig aConfig.setPortalservices(false); aConfig.setGateway(ipaddress); aConfig.setSwversion("01033989"); - aConfig.setLinkbutton(false); + aConfig.setLinkbutton(true); aConfig.setIpaddress(ipaddress); aConfig.setProxyport(0); aConfig.setSwupdate(Swupdate.createSwupdate()); diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index c0cf521..7052416 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -24,6 +24,14 @@ public class UpnpListener { private boolean traceupnp; private BridgeControlDescriptor bridgeControl; private boolean discoveryTemplateLatest; + private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + + "CACHE-CONTROL: max-age=86400\r\n" + + "EXT:\r\n" + + "LOCATION: http://%s:%s/description.xml\r\n" + + "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-001788102201::urn:schemas-upnp-org:device:basic:1\r\n\r\n"; +/* private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n" + @@ -31,6 +39,7 @@ public class UpnpListener { "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:2f402f80-da50-11e1-9b23-001788102201\r\n\r\n"; +*/ /* private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + From bb65650e534039fa39d22f08844d0174e8a37b9b Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 29 Sep 2016 16:21:15 -0500 Subject: [PATCH 07/12] Added ip check if upnp config address is not available on the current host. Added checks for content type and body for put and post requests. --- pom.xml | 2 +- .../bwssystems/HABridge/BridgeSettings.java | 76 ++++++++++++------- .../bwssystems/HABridge/hue/HueMulator.java | 30 +++++--- 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/pom.xml b/pom.xml index 3674447..8aee4b9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0d + 3.1.0e jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index 3e6715c..67d5b8d 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -39,7 +39,6 @@ public class BridgeSettings extends BackupHandler { return theBridgeSettings; } public void buildSettings() { - InetAddress address = null; String addressString = null; String theVeraAddress = null; String theHarmonyAddress = null; @@ -109,34 +108,18 @@ public class BridgeSettings extends BackupHandler { } if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().equals("")) { - try { - log.info("Getting an IP address for this host...."); - Enumeration ifs = NetworkInterface.getNetworkInterfaces(); - - while (ifs.hasMoreElements() && addressString == null) { - NetworkInterface xface = ifs.nextElement(); - Enumeration addrs = xface.getInetAddresses(); - String name = xface.getName(); - int IPsPerNic = 0; - - while (addrs.hasMoreElements() && IPsPerNic == 0) { - address = addrs.nextElement(); - if (InetAddressUtils.isIPv4Address(address.getHostAddress())) { - log.debug(name + " ... has IPV4 addr " + address); - if(!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE)|| !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS)) { - IPsPerNic++; - addressString = address.getHostAddress(); - log.info("Adding " + addressString + " from interface " + name + " as our default upnp config address."); - } - } - } - } - } catch (SocketException e) { - log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e); - return; - } - - theBridgeSettings.setUpnpConfigAddress(addressString); + addressString = checkIpAddress(null, true); + if(addressString != null) { + theBridgeSettings.setUpnpConfigAddress(addressString); + log.info("Adding " + addressString + " as our default upnp config address."); + } + else + log.error("Cannot get ip address of this host."); + } + else { + addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false); + if(addressString == null) + log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host."); } if(theBridgeSettings.getUpnpResponsePort() == null) @@ -258,4 +241,39 @@ public class BridgeSettings extends BackupHandler { return content; } + + private String checkIpAddress(String ipAddress, boolean checkForLocalhost) { + Enumeration ifs = null; + try { + ifs = NetworkInterface.getNetworkInterfaces(); + } catch(SocketException e) { + log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e); + return null; + } + String addressString = null; + InetAddress address = null; + while (ifs.hasMoreElements() && addressString == null) { + NetworkInterface xface = ifs.nextElement(); + Enumeration addrs = xface.getInetAddresses(); + String name = xface.getName(); + int IPsPerNic = 0; + + while (addrs.hasMoreElements() && IPsPerNic == 0) { + address = addrs.nextElement(); + if (InetAddressUtils.isIPv4Address(address.getHostAddress())) { + log.debug(name + " ... has IPV4 addr " + address); + if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) { + IPsPerNic++; + addressString = address.getHostAddress(); + log.debug("checkIpAddress found " + addressString + " from interface " + name); + } + else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){ + addressString = ipAddress; + IPsPerNic++; + } + } + } + } + return addressString; + } } diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 34d5ad1..f665bdb 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -1043,33 +1043,39 @@ public class HueMulator implements HueErrorStringSet { protected String doHttpRequest(String url, String httpVerb, String contentType, String body, NameValue[] headers) { HttpUriRequest request = null; String theContent = null; + ContentType parsedContentType = null; + StringEntity requestBody = null; + if(contentType != null && contentType.length() > 0) { + parsedContentType = ContentType.parse(contentType); + if(body != null && body.length() > 0) + requestBody = new StringEntity(body, parsedContentType); + } + try { if(HttpGet.METHOD_NAME.equalsIgnoreCase(httpVerb) || httpVerb == null) { request = new HttpGet(url); }else if(HttpPost.METHOD_NAME.equalsIgnoreCase(httpVerb)){ HttpPost postRequest = new HttpPost(url); - ContentType parsedContentType = ContentType.parse(contentType); - StringEntity requestBody = new StringEntity(body, parsedContentType); - postRequest.setEntity(requestBody); + if(requestBody != null) + postRequest.setEntity(requestBody); request = postRequest; }else if(HttpPut.METHOD_NAME.equalsIgnoreCase(httpVerb)){ HttpPut putRequest = new HttpPut(url); - ContentType parsedContentType = ContentType.parse(contentType); - StringEntity requestBody = new StringEntity(body, parsedContentType); - putRequest.setEntity(requestBody); + if(requestBody != null) + putRequest.setEntity(requestBody); request = putRequest; } } catch(IllegalArgumentException e) { - log.warn("Error calling out to HA gateway: IllegalArgumentException in log", e); + log.warn("Error creating outbound http request: IllegalArgumentException in log", e); return null; } log.debug("Making outbound call in doHttpRequest: " + request); + if(headers != null && headers.length > 0) { + for(int i = 0; i < headers.length; i++) { + request.setHeader(headers[i].getName(), headers[i].getValue()); + } + } try { - if(headers != null && headers.length > 0) { - for(int i = 0; i < headers.length; i++) { - request.setHeader(headers[i].getName(), headers[i].getValue()); - } - } HttpResponse response; if(url.startsWith("https")) response = httpclientSSL.execute(request); From 7163a12384fc6b88157db9ae754370ae944f6ca8 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 3 Oct 2016 15:07:09 -0500 Subject: [PATCH 08/12] Updating upnp responses to be more inline with what the hue hub does. To do so, added the mac address as the id in the bridge id an, serial number and uuid --- pom.xml | 2 +- .../HABridge/api/hue/HuePublicConfig.java | 1 + .../HABridge/upnp/UpnpListener.java | 31 +++++++++++++++++-- .../HABridge/upnp/UpnpSettingsResource.java | 8 +++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 8aee4b9..f9901a5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0e + 3.1.0i jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java index ac1b1b6..c955cf7 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java @@ -83,6 +83,7 @@ public class HuePublicConfig // } // else // bridgeId = bridgeId + "0800"; + bridgeId = bridgeId.toLowerCase(); return bridgeId; } diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index 7052416..3ed7b79 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -6,7 +6,7 @@ import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.BridgeControlDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.Configuration; -// import com.bwssystems.HABridge.api.hue.HuePublicConfig; +import com.bwssystems.HABridge.api.hue.HuePublicConfig; import java.io.IOException; import java.net.*; @@ -24,6 +24,26 @@ public class UpnpListener { private boolean traceupnp; private BridgeControlDescriptor bridgeControl; private boolean discoveryTemplateLatest; + private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + + "CACHE-CONTROL: max-age=86400\r\n" + + "EXT:\r\n" + + "LOCATION: http://%s:%s/description.xml\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.14.0\r\n" + + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; +/* + private String discoveryTemplate = "NOTIFY * HTTP/1.1\r\n" + + "HOST: %s:%s\r\n" + + "CACHE-CONTROL: max-age=100\r\n" + + "LOCATION: http://%s:%s/description.xml\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.14.0\r\n" + + "NTS: ssdp:alive\r\n" + + "hue-bridgeid: %s\r\n" + + "NT: uuid:2f402f80-da50-11e1-9b23-%s\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; + discoveryResponse = String.format(discoveryTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeIdMac, bridgeIdMac, bridgeIdMac); +*/ +/* private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n" + @@ -31,6 +51,8 @@ public class UpnpListener { "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:2f402f80-da50-11e1-9b23-001788102201::urn:schemas-upnp-org:device:basic:1\r\n\r\n"; + discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort); +*/ /* private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + @@ -245,8 +267,11 @@ public class UpnpListener { protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException { String discoveryResponse = null; - if(discoveryTemplateLatest) - discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort); + String bridgeIdMac = null; + if(discoveryTemplateLatest) { + bridgeIdMac = HuePublicConfig.createConfig("temp", responseAddress).getBridgeid(); + discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, bridgeIdMac); + } else discoveryResponse = String.format(discoveryTemplate091516, responseAddress, httpServerPort); if(traceupnp) { diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java index fd705a3..789653c 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.BridgeSettingsDescriptor; +import com.bwssystems.HABridge.api.hue.HuePublicConfig; import static spark.Spark.get; @@ -31,8 +32,8 @@ public class UpnpSettingsResource { + "Philips hue bridge 2015\n" + "BSB002\n" + "http://www.meethue.com\n" - + "0017880ae670\n" - + "uuid:2f402f80-da50-11e1-9b23-001788102201\n" + + "%s\n" + + "uuid:2f402f80-da50-11e1-9b23-%s\n" + "\n" + "\n" + "(null)\n" @@ -77,7 +78,8 @@ public class UpnpSettingsResource { log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port()); String portNumber = Integer.toString(request.port()); String filledTemplate = null; - filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress()); + String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress()).getBridgeid(); + filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac); if(theSettings.isTraceupnp()) log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber); else From e90e0f69ef7cb79b8223fbdec8527168c61dc7ad Mon Sep 17 00:00:00 2001 From: Admin Date: Tue, 4 Oct 2016 13:34:47 -0500 Subject: [PATCH 09/12] Updated headers content type to be just "application/json" instead of "application/json; charset utf-8". --- pom.xml | 2 +- .../bwssystems/HABridge/hue/HueMulator.java | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index f9901a5..c20860c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0i + 3.1.0j jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index f665bdb..a93d7d5 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -260,7 +260,7 @@ public class HueMulator implements HueErrorStringSet { if(bridgeSettings.isTraceupnp()) log.info("Traceupnp: hue lights list requested: " + userId + " from " + request.ip()); log.debug("hue lights list requested: " + userId + " from " + request.ip()); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.status(HttpStatus.SC_OK); if(validateWhitelistUser(userId, false) == null) { @@ -319,7 +319,7 @@ public class HueMulator implements HueErrorStringSet { response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.header("Access-Control-Allow-Methods", "GET, POST, PUT"); response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); - response.header("Content-Type", "text/html; charset=utf-8"); + response.header("Content-Type", "text/html"); return ""; }); // http://ip_address:port/api with body of user request returns json object for a success of user add @@ -348,7 +348,7 @@ public class HueMulator implements HueErrorStringSet { log.debug("hue api user create requested for device type: " + aDeviceType + " and username: " + newUser); response.header("Access-Control-Allow-Origin", request.headers("Origin")); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); return "[{\"success\":{\"username\":\"" + newUser + "\"}}]"; } ); @@ -359,7 +359,7 @@ public class HueMulator implements HueErrorStringSet { response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.header("Access-Control-Allow-Methods", "GET, POST, PUT"); response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); - response.header("Content-Type", "text/html; charset=utf-8"); + response.header("Content-Type", "text/html"); return ""; }); // http://ip_address:port/api/* with body of user request returns json object for a success of user add - This method is for Harmony Hub @@ -383,7 +383,7 @@ public class HueMulator implements HueErrorStringSet { aDeviceType = ""; log.debug("HH trace: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); return "[{\"success\":{\"username\":\"" + newUser + "\"}}]"; } ); @@ -395,7 +395,7 @@ public class HueMulator implements HueErrorStringSet { log.debug("hue api public config requested, from " + request.ip()); HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue", bridgeSettings.getUpnpConfigAddress()); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.status(HttpStatus.SC_OK); return apiResponse; @@ -404,7 +404,7 @@ public class HueMulator implements HueErrorStringSet { // http://ip_address:port/api/{userId}/config returns json objects for the config get(HUE_CONTEXT + "/:userid/config", "application/json", (request, response) -> { String userId = request.params(":userid"); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.status(HttpStatus.SC_OK); if(bridgeSettings.isTraceupnp()) @@ -426,7 +426,7 @@ public class HueMulator implements HueErrorStringSet { get(HUE_CONTEXT + "/:userid", "application/json", (request, response) -> { String userId = request.params(":userid"); response.header("Access-Control-Allow-Origin", request.headers("Origin")); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); log.debug("hue api full state requested: " + userId + " from " + request.ip()); if(validateWhitelistUser(userId, false) == null) { @@ -456,7 +456,7 @@ public class HueMulator implements HueErrorStringSet { String userId = request.params(":userid"); String lightId = request.params(":id"); response.header("Access-Control-Allow-Origin", request.headers("Origin")); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + request.ip()); if(validateWhitelistUser(userId, false) == null) { @@ -520,7 +520,7 @@ public class HueMulator implements HueErrorStringSet { response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.header("Access-Control-Allow-Methods", "GET, POST, PUT"); response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); - response.header("Content-Type", "text/html; charset=utf-8"); + response.header("Content-Type", "text/html"); return ""; }); // http://ip_address:port/api/{userId}/lights/{lightId}/bridgeupdatestate uses json object to update the internal bridge lights state. @@ -535,7 +535,7 @@ public class HueMulator implements HueErrorStringSet { boolean stateHasBriInc = false; log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body()); response.header("Access-Control-Allow-Origin", request.headers("Origin")); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); if(validateWhitelistUser(userId, false) == null) { log.debug("Valudate user, No User supplied"); @@ -604,7 +604,7 @@ public class HueMulator implements HueErrorStringSet { response.header("Access-Control-Allow-Origin", request.headers("Origin")); response.header("Access-Control-Allow-Methods", "GET, POST, PUT"); response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); - response.header("Content-Type", "text/html; charset=utf-8"); + response.header("Content-Type", "text/html"); return ""; }); // http://ip_address:port/api/{userId}/lights/{lightId}/state uses json object to set the lights state @@ -624,7 +624,7 @@ public class HueMulator implements HueErrorStringSet { boolean stateHasBriInc = false; log.debug("hue state change requested: " + userId + " from " + request.ip() + " body: " + request.body()); response.header("Access-Control-Allow-Origin", request.headers("Origin")); - response.type("application/json; charset=utf-8"); + response.type("application/json"); response.status(HttpStatus.SC_OK); if(validateWhitelistUser(userId, false) == null) { log.debug("Valudate user, No User supplied"); From 004276d3ea508eec137eb1af0ec446cbdf5b9cf4 Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 7 Oct 2016 16:29:55 -0500 Subject: [PATCH 10/12] Changed handling of upnp rsponses from the upnp listener and the huemulator udp calls to use the same response port. --- pom.xml | 2 +- .../com/bwssystems/HABridge/HABridge.java | 38 +++++++++++------- .../bwssystems/HABridge/hue/HueMulator.java | 12 +++--- .../HABridge/upnp/UpnpListener.java | 39 ++++--------------- 4 files changed, 37 insertions(+), 54 deletions(-) diff --git a/pom.xml b/pom.xml index c20860c..8cf2371 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0j + 3.1.0k jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/HABridge.java b/src/main/java/com/bwssystems/HABridge/HABridge.java index 928570c..ae4ab4f 100644 --- a/src/main/java/com/bwssystems/HABridge/HABridge.java +++ b/src/main/java/com/bwssystems/HABridge/HABridge.java @@ -13,6 +13,7 @@ import com.bwssystems.NestBridge.NestHome; import com.bwssystems.hal.HalHome; import com.bwssystems.harmony.HarmonyHome; import com.bwssystems.hue.HueHome; +import com.bwssystems.util.UDPDatagramSender; public class HABridge { @@ -39,6 +40,7 @@ public class HABridge { HueHome hueHome; HalHome halHome; HueMulator theHueMulator; + UDPDatagramSender udpSender; UpnpSettingsResource theSettingResponder; UpnpListener theUpnpListener; SystemControl theSystem; @@ -72,29 +74,37 @@ public class HABridge { halHome = new HalHome(bridgeSettings.getBridgeSettingsDescriptor()); // setup the class to handle the resource setup rest api theResources = new DeviceResource(bridgeSettings.getBridgeSettingsDescriptor(), harmonyHome, nestHome, hueHome, halHome); - // setup the class to handle the hue emulator rest api - 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()); theSettingResponder.setupServer(); - // wait for the sparkjava initialization of the rest api classes to be complete - awaitInitialization(); - - // start the upnp ssdp discovery listener - theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl()); - if(theUpnpListener.startListening()) - log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted...."); - else - bridgeSettings.getBridgeControl().setStop(true); - if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged()) - bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor()); + // setup the UDP Datagram socket to be used by the HueMulator and the upnpListener + udpSender = UDPDatagramSender.createUDPDatagramSender(bridgeSettings.getBridgeSettingsDescriptor().getUpnpResponsePort()); + if(udpSender == null) { + bridgeSettings.getBridgeControl().setStop(true); + } + else { + // setup the class to handle the hue emulator rest api + theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), harmonyHome, nestHome, hueHome, udpSender); + theHueMulator.setupServer(); + // wait for the sparkjava initialization of the rest api classes to be complete + awaitInitialization(); + + // start the upnp ssdp discovery listener + theUpnpListener = new UpnpListener(bridgeSettings.getBridgeSettingsDescriptor(), bridgeSettings.getBridgeControl(), udpSender); + if(theUpnpListener.startListening()) + log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted...."); + else + bridgeSettings.getBridgeControl().setStop(true); + if(bridgeSettings.getBridgeSettingsDescriptor().isSettingsChanged()) + bridgeSettings.save(bridgeSettings.getBridgeSettingsDescriptor()); + } bridgeSettings.getBridgeControl().setReinit(false); stop(); nestHome.closeTheNest(); nestHome = null; harmonyHome.shutdownHarmonyHubs(); harmonyHome = null; + udpSender.closeResponseSocket(); } log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting...."); System.exit(0); diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index a93d7d5..60bca51 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -27,6 +27,7 @@ import com.bwssystems.hue.HueHome; import com.bwssystems.hue.HueUtil; import com.bwssystems.nest.controller.Nest; import com.bwssystems.util.JsonTransformer; +import com.bwssystems.util.UDPDatagramSender; import com.google.gson.Gson; import net.java.dev.eval.Expression; @@ -60,8 +61,6 @@ import java.io.DataOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; -import java.net.DatagramPacket; -import java.net.DatagramSocket; import java.net.InetAddress; import java.net.Socket; import java.nio.charset.Charset; @@ -99,12 +98,13 @@ public class HueMulator implements HueErrorStringSet { private SSLConnectionSocketFactory sslsf; private RequestConfig globalConfig; private BridgeSettingsDescriptor bridgeSettings; + private UDPDatagramSender theUDPDatagramSender; private byte[] sendData; private String hueUser; private String errorString; - public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome){ + public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HarmonyHome theHarmonyHome, NestHome aNestHome, HueHome aHueHome, UDPDatagramSender aUdpDatagramSender) { httpClient = HttpClients.createDefault(); // Trust own CA and all self-signed certs sslcontext = SSLContexts.createDefault(); @@ -136,6 +136,7 @@ public class HueMulator implements HueErrorStringSet { else this.myHueHome = null; bridgeSettings = theBridgeSettings; + theUDPDatagramSender = aUdpDatagramSender; hueUser = null; errorString = null; } @@ -905,10 +906,7 @@ public class HueMulator implements HueErrorStringSet { } if(callItems[i].getItem().contains("udp://")) { log.debug("executing HUE api request to UDP: " + callItems[i].getItem()); - DatagramSocket responseSocket = new DatagramSocket(Integer.parseInt(port)); - DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, Integer.parseInt(port)); - responseSocket.send(sendPacket); - responseSocket.close(); + theUDPDatagramSender.sendUDPResponse(new String(sendData), IPAddress, Integer.parseInt(port)); } else if(callItems[i].getItem().contains("tcp://")) { diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index 3ed7b79..dba4225 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -7,6 +7,7 @@ import com.bwssystems.HABridge.BridgeControlDescriptor; import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.Configuration; import com.bwssystems.HABridge.api.hue.HuePublicConfig; +import com.bwssystems.util.UDPDatagramSender; import java.io.IOException; import java.net.*; @@ -17,7 +18,7 @@ import org.apache.http.conn.util.*; public class UpnpListener { private Logger log = LoggerFactory.getLogger(UpnpListener.class); - private int upnpResponsePort; + private UDPDatagramSender theUDPDatagramSender; private int httpServerPort; private String responseAddress; private boolean strict; @@ -92,9 +93,9 @@ public class UpnpListener { "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n"; */ - public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl) { + public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) { super(); - upnpResponsePort = theSettings.getUpnpResponsePort(); + theUDPDatagramSender = aUdpDatagramSender; httpServerPort = Integer.valueOf(theSettings.getServerPort()); responseAddress = theSettings.getUpnpConfigAddress(); strict = theSettings.isUpnpStrict(); @@ -106,33 +107,9 @@ public class UpnpListener { @SuppressWarnings("resource") public boolean startListening(){ log.info("UPNP Discovery Listener starting...."); - DatagramSocket responseSocket = null; MulticastSocket upnpMulticastSocket = null; Enumeration ifs = null; - boolean portLoopControl = true; - int retryCount = 0; - while(portLoopControl) { - try { - responseSocket = new DatagramSocket(upnpResponsePort); - if(retryCount > 0) - log.info("Upnp Response Port issue, found open port: " + upnpResponsePort); - portLoopControl = false; - } catch(SocketException e) { - if(retryCount == 0) - log.warn("Upnp Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + upnpResponsePort); - if(retryCount >= 20) { - portLoopControl = false; - log.error("Upnp Response Port issue, could not find open port - last port tried: " + upnpResponsePort + " with message: " + e.getMessage()); - return false; - } - } - if(portLoopControl) { - retryCount++; - upnpResponsePort++; - } - } - try { upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT); } catch(IOException e){ @@ -189,7 +166,7 @@ public class UpnpListener { upnpMulticastSocket.receive(packet); if (isSSDPDiscovery(packet)) { try { - sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort()); + sendUpnpResponse(packet.getAddress(), packet.getPort()); } catch (IOException e) { if(e.getMessage().equalsIgnoreCase("Host is down")) log.warn("UpnpListener encountered an error sending upnp response packet as requesting host is now not available. IP: " + packet.getAddress().getHostAddress()); @@ -213,7 +190,6 @@ public class UpnpListener { } } upnpMulticastSocket.close(); - responseSocket.close(); if (bridgeControl.isReinit()) log.info("UPNP Discovery Listener - ended, restart found"); if (bridgeControl.isStop()) @@ -265,7 +241,7 @@ public class UpnpListener { return false; } - protected void sendUpnpResponse(DatagramSocket socket, InetAddress requester, int sourcePort) throws IOException { + protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException { String discoveryResponse = null; String bridgeIdMac = null; if(discoveryTemplateLatest) { @@ -280,7 +256,6 @@ public class UpnpListener { } else log.debug("sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort); - DatagramPacket response = new DatagramPacket(discoveryResponse.getBytes(), discoveryResponse.length(), requester, sourcePort); - socket.send(response); + theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort); } } From 23d43b0136afac7404d4158d1555ceb66824741e Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 10 Oct 2016 13:23:29 -0500 Subject: [PATCH 11/12] I got some more info from another source about the discovery responses of a real hue. It sends three responses not just one. Also I have updated the S/N UUID and the Hue Bridge ID creation as they are different. --- pom.xml | 2 +- .../HABridge/api/hue/HueConfig.java | 6 +- .../HABridge/api/hue/HuePublicConfig.java | 36 +++--- .../HABridge/upnp/UpnpListener.java | 108 +++++++----------- .../HABridge/upnp/UpnpSettingsResource.java | 2 +- .../bwssystems/util/UDPDatagramSender.java | 72 ++++++++++++ 6 files changed, 133 insertions(+), 93 deletions(-) create mode 100644 src/main/java/com/bwssystems/util/UDPDatagramSender.java diff --git a/pom.xml b/pom.xml index 8cf2371..45f78a9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0k + 3.1.0l jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java index 705e1c5..6389d73 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HueConfig.java @@ -40,10 +40,10 @@ public class HueConfig SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC")); aConfig.setMac(HueConfig.getMacAddress(ipaddress)); - aConfig.setApiversion("1.14.0"); + aConfig.setApiversion("1.15.0"); aConfig.setPortalservices(false); aConfig.setGateway(ipaddress); - aConfig.setSwversion("01033989"); + aConfig.setSwversion("01035934"); aConfig.setLinkbutton(true); aConfig.setIpaddress(ipaddress); aConfig.setProxyport(0); @@ -56,7 +56,7 @@ public class HueConfig aConfig.setLocaltime(dateFormat.format(new Date())); aConfig.setTimezone(TimeZone.getDefault().getID()); aConfig.setZigbeechannel("6"); - aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress)); + aConfig.setBridgeid(HuePublicConfig.createConfig(name, ipaddress).getHueBridgeIdFromMac()); aConfig.setModelid("BSB002"); aConfig.setFactorynew(false); aConfig.setReplacesbridgeid(null); diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java index c955cf7..9492ba9 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HuePublicConfig.java @@ -1,13 +1,11 @@ package com.bwssystems.HABridge.api.hue; -import java.math.BigInteger; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.StringTokenizer; -import javax.xml.bind.DatatypeConverter; public class HuePublicConfig { @@ -23,10 +21,10 @@ public class HuePublicConfig public static HuePublicConfig createConfig(String name, String ipaddress) { HuePublicConfig aConfig = new HuePublicConfig(); aConfig.setMac(HuePublicConfig.getMacAddress(ipaddress)); - aConfig.setApiversion("1.14.0"); - aConfig.setSwversion("01033989"); + aConfig.setApiversion("1.15.0"); + aConfig.setSwversion("01035934"); aConfig.setName(name); - aConfig.setBridgeid(HuePublicConfig.getBridgeIdFromMac(aConfig.getMac(), ipaddress)); + aConfig.setBridgeid(aConfig.getHueBridgeIdFromMac()); aConfig.setModelid("BSB002"); aConfig.setFactorynew(false); aConfig.setReplacesbridgeid(null); @@ -67,24 +65,22 @@ public class HuePublicConfig return sb.toString(); } - protected static String getBridgeIdFromMac(String macAddr, String ipAddr) + public String getSNUUIDFromMac() { - StringTokenizer st = new StringTokenizer(macAddr, ":"); - String bridgeId = ""; -// String port = null; + StringTokenizer st = new StringTokenizer(this.getMac(), ":"); + String bridgeUUID = ""; while(st.hasMoreTokens()) { - bridgeId = bridgeId + st.nextToken(); + bridgeUUID = bridgeUUID + st.nextToken(); } -// if(ipAddr.contains(":")) { -// port = ipAddr.substring(ipAddr.indexOf(":")); -// BigInteger bigInt = BigInteger.valueOf(Integer.getInteger(port).intValue()); -// byte[] theBytes = bigInt.toByteArray(); -// bridgeId = bridgeId + DatatypeConverter.printHexBinary(theBytes); -// } -// else -// bridgeId = bridgeId + "0800"; - bridgeId = bridgeId.toLowerCase(); - return bridgeId; + bridgeUUID = bridgeUUID.toLowerCase(); + return bridgeUUID.toLowerCase(); + } + + protected String getHueBridgeIdFromMac() + { + String cleanMac = this.getSNUUIDFromMac(); + String bridgeId = cleanMac.substring(0, 6) + "FFFE" + cleanMac.substring(6); + return bridgeId.toUpperCase(); } public String getMac() { diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index dba4225..bff33cb 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -24,75 +24,34 @@ public class UpnpListener { private boolean strict; private boolean traceupnp; private BridgeControlDescriptor bridgeControl; - private boolean discoveryTemplateLatest; - private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + - "EXT:\r\n" + - "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.14.0\r\n" + - "ST: urn:schemas-upnp-org:device:basic:1\r\n" + - "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; -/* - private String discoveryTemplate = "NOTIFY * HTTP/1.1\r\n" + + private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n" + + "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.14.0\r\n" + - "NTS: ssdp:alive\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" + "hue-bridgeid: %s\r\n" + - "NT: uuid:2f402f80-da50-11e1-9b23-%s\r\n" + - "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; - discoveryResponse = String.format(discoveryTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeIdMac, bridgeIdMac, bridgeIdMac); -*/ -/* - private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + - "EXT:\r\n" + - "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + - "ST: urn:schemas-upnp-org:device:basic:1\r\n" + - "USN: uuid:2f402f80-da50-11e1-9b23-001788102201::urn:schemas-upnp-org:device:basic:1\r\n\r\n"; - discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort); -*/ -/* - private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + - "EXT:\r\n" + - "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + "ST: upnp:rootdevice\r\n" + - "USN: uuid:2f402f80-da50-11e1-9b23-001788102201\r\n\r\n"; -*/ -/* - private String discoveryTemplate = "HTTP/1.1 200 OK\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-%s::upnp:rootdevice\r\n\r\n"; + private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.10.0\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" + "hue-bridgeid: %s\r\n" + - "ST: upnp:rootdevice\r\n" + - "USN: uuid:2f402f80-da50-11e1-9b23-001788102201\r\n\r\n"; - - discoveryResponse = String.format(discoveryTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, HuePublicConfig.createConfig("temp", responseAddress).getBridgeid()); -*/ - private String discoveryTemplate091516 = "HTTP/1.1 200 OK\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + + "ST: uuid:2f402f80-da50-11e1-9b23-%s\r\n" + + "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; + private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" + + "HOST: %s:%s\r\n" + + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + - "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.10.0\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.15.0\r\n" + + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + - "USN: uuid:Socket-1_0-221438K0100073::urn:schemas-upnp-org:device:basic:1\r\n\r\n"; -/* - private String discoveryTemplateOld = "HTTP/1.1 200 OK\r\n" + - "CACHE-CONTROL: max-age=86400\r\n" + - "EXT:\r\n" + - "LOCATION: http://%s:%s/upnp/amazon-ha-bridge/setup.xml\r\n" + - "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + - "01-NLS: %s\r\n" + - "ST: urn:schemas-upnp-org:device:basic:1\r\n" + - "USN: uuid:Socket-1_0-221438K0100073::urn:Belkin:device:**\r\n\r\n"; -*/ + "USN: uuid:2f402f80-da50-11e1-9b23-%s\r\n\r\n"; + public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) { super(); theUDPDatagramSender = aUdpDatagramSender; @@ -101,7 +60,6 @@ public class UpnpListener { strict = theSettings.isUpnpStrict(); traceupnp = theSettings.isTraceupnp(); bridgeControl = theControl; - discoveryTemplateLatest = true; } @SuppressWarnings("resource") @@ -243,19 +201,33 @@ public class UpnpListener { protected void sendUpnpResponse(InetAddress requester, int sourcePort) throws IOException { String discoveryResponse = null; - String bridgeIdMac = null; - if(discoveryTemplateLatest) { - bridgeIdMac = HuePublicConfig.createConfig("temp", responseAddress).getBridgeid(); - discoveryResponse = String.format(discoveryTemplate, responseAddress, httpServerPort, bridgeIdMac); - } - else - discoveryResponse = String.format(discoveryTemplate091516, responseAddress, httpServerPort); + String bridgeId = null; + String bridgeSNUUID = null; + HuePublicConfig aHueConfig = HuePublicConfig.createConfig("temp", responseAddress); + bridgeId = aHueConfig.getBridgeid(); + bridgeSNUUID = aHueConfig.getSNUUIDFromMac(); + discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID); if(traceupnp) { - log.info("Traceupnp: sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort); - log.info("Traceupnp: discoveryResponse is <<<" + discoveryResponse + ">>>"); + log.info("Traceupnp: sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>"); } else - log.debug("sendUpnpResponse discovery template with address: " + responseAddress + " and port: " + httpServerPort); + log.debug("sendUpnpResponse discovery responseTemplate1 is <<<" + discoveryResponse + ">>>"); + theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort); + + discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID); + if(traceupnp) { + log.info("Traceupnp: sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); + } + else + log.debug("sendUpnpResponse discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); + theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort); + + discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, responseAddress, httpServerPort, bridgeId, bridgeSNUUID); + if(traceupnp) { + log.info("Traceupnp: sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); + } + else + log.debug("sendUpnpResponse discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); theUDPDatagramSender.sendUDPResponse(discoveryResponse, requester, sourcePort); } } diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java index 789653c..528b4e9 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java @@ -78,7 +78,7 @@ public class UpnpSettingsResource { log.debug("upnp device settings requested: " + " from " + request.ip() + ":" + request.port()); String portNumber = Integer.toString(request.port()); String filledTemplate = null; - String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress()).getBridgeid(); + String bridgeIdMac = HuePublicConfig.createConfig("temp", theSettings.getUpnpConfigAddress()).getSNUUIDFromMac(); filledTemplate = String.format(hueTemplate, theSettings.getUpnpConfigAddress(), portNumber, theSettings.getUpnpConfigAddress(), bridgeIdMac, bridgeIdMac); if(theSettings.isTraceupnp()) log.info("Traceupnp: upnp device settings template filled with address: " + theSettings.getUpnpConfigAddress() + " and port: " + portNumber); diff --git a/src/main/java/com/bwssystems/util/UDPDatagramSender.java b/src/main/java/com/bwssystems/util/UDPDatagramSender.java new file mode 100644 index 0000000..62b2931 --- /dev/null +++ b/src/main/java/com/bwssystems/util/UDPDatagramSender.java @@ -0,0 +1,72 @@ +package com.bwssystems.util; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class UDPDatagramSender { + private Logger log = LoggerFactory.getLogger(UDPDatagramSender.class); + private DatagramSocket responseSocket = null; + private int udpResponsePort; + + public UDPDatagramSender() { + super(); + udpResponsePort = 0; + } + + public static UDPDatagramSender createUDPDatagramSender(int udpResponsePort) { + UDPDatagramSender aDatagramSender = new UDPDatagramSender(); + if(aDatagramSender.initializeSocket(udpResponsePort)) + return aDatagramSender; + else + return null; + } + + private boolean initializeSocket(int port) { + log.info("Initializing UDP response Seocket..."); + udpResponsePort = port; + boolean portLoopControl = true; + int retryCount = 0; + while(portLoopControl) { + try { + responseSocket = new DatagramSocket(udpResponsePort); + portLoopControl = false; + } catch(SocketException e) { + if(retryCount == 0) + log.warn("UDP Response Port is in use, starting loop to find open port for 20 tries - configured port is: " + udpResponsePort); + if(retryCount >= 20) { + portLoopControl = false; + log.error("UDP Response Port issue, could not find open port - last port tried: " + udpResponsePort + " with message: " + e.getMessage()); + return false; + } + } + if(portLoopControl) { + retryCount++; + udpResponsePort++; + } + } + log.info("UDP response Seocket initialized to: " + udpResponsePort); + return true; + } + + public int getUdpResponsePort() { + return udpResponsePort; + } + + public void closeResponseSocket() { + responseSocket.close(); + } + + public void sendUDPResponse(String udpResponse, InetAddress requester, int sourcePort) throws IOException { + log.debug("Sending response string: <<<" + udpResponse + ">>>"); + if(responseSocket == null) + throw new IOException("Socket not initialized"); + DatagramPacket response = new DatagramPacket(udpResponse.getBytes(), udpResponse.length(), requester, sourcePort); + responseSocket.send(response); + } +} From 7e9600d2a7629495e137b4668bef31a07225e131 Mon Sep 17 00:00:00 2001 From: Admin Date: Tue, 18 Oct 2016 08:35:27 -0500 Subject: [PATCH 12/12] Update release version number. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45f78a9..acfe3a0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 3.1.0l + 3.2.0 jar HA Bridge