diff --git a/README.md b/README.md index 0afefd2..7f6eea2 100644 --- a/README.md +++ b/README.md @@ -57,20 +57,20 @@ Then locate the jar and start the server with: ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below. ``` -java -jar ha-bridge-5.3.1RC2.jar +java -jar ha-bridge-5.3.1RC3.jar ``` ## Manual installation of ha-bridge and setup of systemd service Next gen Linux systems (this includes the Raspberry Pi), use systemd to run and manage services. Here is a link on how to use systemd: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units -Create the directory and make sure that ha-bridge-5.3.1RC2.jar is in your /home/pi/ha-bridge directory. +Create the directory and make sure that ha-bridge-5.3.1RC3.jar is in your /home/pi/ha-bridge directory. ``` pi@raspberrypi:~ $ mkdir ha-bridge pi@raspberrypi:~ $ cd ha-bridge -pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.1RC2/ha-bridge-5.3.1RC2.jar +pi@raspberrypi:~/ha-bridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v5.3.1RC3/ha-bridge-5.3.1RC3.jar ``` Create the ha-bridge.service unit file: @@ -89,7 +89,7 @@ After=network.target Type=simple WorkingDirectory=/home/pi/ha-bridge -ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.3.1RC2.jar +ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/ha-bridge/data/habridge.config /home/pi/ha-bridge/ha-bridge-5.3.1RC3.jar [Install] WantedBy=multi-user.target diff --git a/pom.xml b/pom.xml index 4a110a5..527f745 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 5.3.1RC1 + 5.3.1RC3 jar HA Bridge @@ -172,7 +172,7 @@ - + 3.6 @@ -184,7 +184,7 @@ maven-compiler-plugin 3.8.1 - + 11 diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index f2026a8..1af0d29 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -252,7 +252,7 @@ public class BridgeSettings extends BackupHandler { theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class); theBridgeSettings.setConfigfile(aPath.toString()); } catch (Exception e) { - log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed."); + log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed. Using default settings."); theBridgeSettings = new BridgeSettingsDescriptor(); } } diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java index 31f4623..e714038 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java @@ -132,6 +132,9 @@ public class BridgeSettingsDescriptor { @SerializedName("haaddressessecured") @Expose private boolean haaddressessecured; + @SerializedName("upnpadvanced") + @Expose + private boolean upnpadvanced; // @SerializedName("activeloggers") // @Expose // private List activeloggers; @@ -192,6 +195,8 @@ public class BridgeSettingsDescriptor { this.upnporiginal = false; this.seedid = 100; this.haaddressessecured = false; + this.configfile = Configuration.CONFIG_FILE; + this.upnpadvanced = false; } public String getUpnpConfigAddress() { @@ -847,4 +852,12 @@ public class BridgeSettingsDescriptor { public void setHaaddressessecured(boolean haaddressessecured) { this.haaddressessecured = haaddressessecured; } + + public boolean isUpnpadvanced() { + return upnpadvanced; + } + + public void setUpnpadvanced(boolean upnpadvanced) { + this.upnpadvanced = upnpadvanced; + } } diff --git a/src/main/java/com/bwssystems/HABridge/api/hue/HueConstants.java b/src/main/java/com/bwssystems/HABridge/api/hue/HueConstants.java index 72f136d..2782d46 100644 --- a/src/main/java/com/bwssystems/HABridge/api/hue/HueConstants.java +++ b/src/main/java/com/bwssystems/HABridge/api/hue/HueConstants.java @@ -2,7 +2,7 @@ package com.bwssystems.HABridge.api.hue; public class HueConstants { public final static String HUB_VERSION = "9999999999"; - public final static String API_VERSION = "1.19.0"; + public final static String API_VERSION = "1.17.0"; public final static String MODEL_ID = "BSB002"; public final static String UUID_PREFIX = "2f402f80-da50-11e1-9b23-"; } diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java index 5526a49..15b2afb 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java @@ -30,6 +30,8 @@ import com.google.gson.JsonSyntaxException; import java.util.Collection; import java.util.List; import java.util.Arrays; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; /* * This is an in memory list to manage the configured devices and saves the list as a JSON string to a file for later @@ -188,7 +190,7 @@ public class DeviceRepository extends BackupHandler { nextId++; } if (descriptors[i].getUniqueid() == null || descriptors[i].getUniqueid().length() == 0) { - descriptors[i].setUniqueid("00:11:22:33:44:55:66:" + hueUniqueId(Integer.valueOf(descriptors[i].getId()))); + descriptors[i].setUniqueid(hueUniqueId(Integer.valueOf(descriptors[i].getId()))); } put(descriptors[i].getId(), descriptors[i]); theNames = theNames + " " + descriptors[i].getName() + ", "; @@ -206,11 +208,10 @@ public class DeviceRepository extends BackupHandler { DeviceDescriptor theDevice; boolean findNext = true; - nextId = seedId; - while(deviceIterator.hasNext()) { + while (deviceIterator.hasNext()) { theDevice = deviceIterator.next(); - if(theDevice.isLockDeviceId()) { + if (theDevice.isLockDeviceId()) { lockedIds.add(theDevice.getId()); } } @@ -220,15 +221,15 @@ public class DeviceRepository extends BackupHandler { theDevice = deviceIterator.next(); if (!theDevice.isLockDeviceId()) { findNext = true; - while(findNext) { - if(lockedIds.contains(String.valueOf(nextId))) { + while (findNext) { + if (lockedIds.contains(String.valueOf(nextId))) { nextId++; } else { findNext = false; } } theDevice.setId(String.valueOf(nextId)); - theDevice.setUniqueid("00:11:22:33:44:55:66:" + hueUniqueId(nextId)); + theDevice.setUniqueid(hueUniqueId(nextId)); nextId++; } newdevices.put(theDevice.getId(), theDevice); @@ -297,27 +298,51 @@ public class DeviceRepository extends BackupHandler { } private String hueUniqueId(Integer anId) { - String theUniqueId; + String theUniqueId = null; Integer newValue; String hexValueLeft; String hexValueRight; - newValue = anId % 256; - if (newValue <= 0) - newValue = 1; - else if (newValue > 255) - newValue = 255; - hexValueLeft = HexLibrary.byteToHex(newValue.byteValue()); - newValue = anId / 256; - newValue = newValue % 256; - if (newValue < 0) - newValue = 0; - else if (newValue > 255) - newValue = 255; - hexValueRight = HexLibrary.byteToHex(newValue.byteValue()); + MessageDigest md = null; - theUniqueId = String.format("%s-%s", hexValueLeft, hexValueRight).toUpperCase(); + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + log.warn("Cannot get MD5 utility to hash unique ids."); + } + if(md != null) { + md.update(anId.toString().getBytes()); + byte[] digest = md.digest(); + theUniqueId = String.format("%s:%s:%s:%s:%s:%s:%s-%s", + HexLibrary.encodeHexString(digest).substring(0, 2), + HexLibrary.encodeHexString(digest).substring(2, 4), + HexLibrary.encodeHexString(digest).substring(4, 6), + HexLibrary.encodeHexString(digest).substring(6, 8), + HexLibrary.encodeHexString(digest).substring(8, 10), + HexLibrary.encodeHexString(digest).substring(10, 12), + HexLibrary.encodeHexString(digest).substring(12, 14), + HexLibrary.encodeHexString(digest).substring(14, 16)); + } + + + if(theUniqueId == null) { + newValue = anId % 256; + if (newValue <= 0) + newValue = 1; + else if (newValue > 255) + newValue = 255; + hexValueLeft = HexLibrary.byteToHex(newValue.byteValue()); + newValue = anId / 256; + newValue = newValue % 256; + if (newValue < 0) + newValue = 0; + else if (newValue > 255) + newValue = 255; + hexValueRight = HexLibrary.byteToHex(newValue.byteValue()); + + theUniqueId = String.format("11:22:33:44:55:66:%s-%s", hexValueLeft, hexValueRight).toUpperCase(); + } return theUniqueId; } } diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index a2ab7a0..da85b41 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -30,6 +30,7 @@ public class UpnpListener { private String upnpConfigIP; // private boolean strict; private boolean upnpOriginal; + private boolean upnpAdvanced; private boolean traceupnp; private boolean useUpnpIface; private BridgeControlDescriptor bridgeControl; @@ -38,31 +39,37 @@ public class UpnpListener { private String httpType; private HuePublicConfig aHueConfig; private Integer theUpnpSendDelay; + + /* This is the minimum response needed, all others are for the advanced setting */ 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: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" - + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:" - + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n"; + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; + + /* These next 2 templates are for the advanced upnp option */ private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n" - + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%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: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" - + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" - + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; + + "EXT:\r\n" + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + + HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:" + + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n"; + + /* These notify templates are for the advanced upnp option */ private String notifyTemplate1 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n" - + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; private String notifyTemplate2 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n" - + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: upnp:rootdevice\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n"; private String notifyTemplate3 = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n" - + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + + "LOCATION: %s://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/" + HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n"; @@ -75,6 +82,7 @@ public class UpnpListener { upnpConfigIP = theSettings.getBridgeSettingsDescriptor().getUpnpConfigAddress(); // strict = theSettings.isUpnpStrict(); upnpOriginal = theSettings.getBridgeSettingsDescriptor().isUpnporiginal(); + upnpAdvanced = theSettings.getBridgeSettingsDescriptor().isUpnpadvanced(); traceupnp = theSettings.getBridgeSettingsDescriptor().isTraceupnp(); useUpnpIface = theSettings.getBridgeSettingsDescriptor().isUseupnpiface(); theUpnpSendDelay = theSettings.getBridgeSettingsDescriptor().getUpnpsenddelay(); @@ -192,10 +200,13 @@ public class UpnpListener { log.info("UPNP Discovery Listener running and ready...."); boolean loopControl = true; boolean error = false; - try { - upnpMulticastSocket.setSoTimeout((int) Configuration.UPNP_NOTIFY_TIMEOUT); - } catch (SocketException e1) { - log.warn("Could not sent soTimeout on multi-cast socket"); + + if(upnpAdvanced) { + try { + upnpMulticastSocket.setSoTimeout((int) Configuration.UPNP_NOTIFY_TIMEOUT); + } catch (SocketException e1) { + log.warn("Could not sent soTimeout on multi-cast socket"); + } } // Instant current, previous; // previous = Instant.now(); @@ -326,37 +337,39 @@ public class UpnpListener { + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>"); sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); - try { - Thread.sleep(theUpnpSendDelay); - } catch (InterruptedException e) { - // noop - } - discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, - Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, - bridgeSNUUID, bridgeSNUUID); - if (traceupnp) { - log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" - + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort); - } - log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort - + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); - sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); + if(upnpAdvanced) { + try { + Thread.sleep(theUpnpSendDelay); + } catch (InterruptedException e) { + // noop + } + discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, + Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, + bridgeSNUUID, bridgeSNUUID); + if (traceupnp) { + log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" + + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort); + } + log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort + + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>"); + sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); - try { - Thread.sleep(theUpnpSendDelay); - } catch (InterruptedException e) { - // noop + try { + Thread.sleep(theUpnpSendDelay); + } catch (InterruptedException e) { + // noop + } + discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, + Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, + bridgeSNUUID); + if (traceupnp) { + log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" + + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort); + } + log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort + + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); + sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); } - discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, - Configuration.UPNP_DISCOVERY_PORT, httpType, httpLocationAddress, httpServerPort, bridgeId, - bridgeSNUUID); - if (traceupnp) { - log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" - + httpServerPort + " to address: " + requester.getHostAddress() + ":" + sourcePort); - } - log.debug("sendUpnpResponse to address: " + requester.getHostAddress() + ":" + sourcePort - + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>"); - sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort); } private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException { diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java index 58c692d..57664f8 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java @@ -55,10 +55,12 @@ public class UpnpSettingsResource { + "24\n" + "hue_logo_3.png\n" + "\n" - + "\n" - + "\n" - + "\n"; + + "\n"; + private String hueTemplate_end = "\n" + + "\n"; + + /* not utilizing this section any more private String hueTemplate_mid_orig = "\n" + "\n" + "(null)\n" @@ -68,7 +70,7 @@ public class UpnpSettingsResource { + "(null)\n" + "\n" + "\n"; - + */ public UpnpSettingsResource(BridgeSettings theBridgeSettings) { super(); @@ -92,16 +94,24 @@ public class UpnpSettingsResource { String hueTemplate = null; if(theSettings.isUpnporiginal()) { httpLocationAddr = theSettings.getUpnpConfigAddress(); - hueTemplate = hueTemplate_pre + hueTemplate_mid_orig + hueTemplate_post; - } else { - + hueTemplate = hueTemplate_pre + hueTemplate_end; + } else if(!theSettings.isUpnpadvanced()) { if(theSettings.isUseupnpiface()) { httpLocationAddr = theSettings.getUpnpConfigAddress(); } else { log.debug("Get Outbound address for ip:" + request.ip() + " and port:" + request.port()); httpLocationAddr = AddressUtil.getOutboundAddress(request.ip(), request.port()).getHostAddress(); } - hueTemplate = hueTemplate_pre + hueTemplate_post; + hueTemplate = hueTemplate_pre + hueTemplate_end; + } else { + + if(theSettings.isUseupnpiface()) { + httpLocationAddr = theSettings.getUpnpConfigAddress(); + } else { + log.debug("Get Outbound address for ip:" + request.ip() + " and port:" + request.port()); + httpLocationAddr = AddressUtil.getOutboundAddress(request.ip(), request.port()).getHostAddress(); + } + hueTemplate = hueTemplate_pre + hueTemplate_post + hueTemplate_end; } String bridgeIdMac = HuePublicConfig.createConfig("temp", httpLocationAddr, HueConstants.HUB_VERSION, theSettings.getHubmac()).getSNUUIDFromMac(); diff --git a/src/main/resources/public/views/system.html b/src/main/resources/public/views/system.html index e0397a9..34f16b4 100644 --- a/src/main/resources/public/views/system.html +++ b/src/main/resources/public/views/system.html @@ -824,6 +824,11 @@ {{bridge.settings.upnporiginal}} + + UPNP Advanced (use multiple responses and notifies) + {{bridge.settings.upnpadvanced}} + Trace UPNP Calls