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); + } +}