diff --git a/pom.xml b/pom.xml index 88faba8..623a4ea 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 1.3.8 + 1.3.8a jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index eeb0846..f931859 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -18,6 +18,9 @@ public class BridgeSettings { private String nestuser; private String nestpwd; private boolean nestconfigured; + private String configfile; + private boolean restart; + private boolean stop; public String getUpnpConfigAddress() { return upnpconfigaddress; @@ -109,6 +112,24 @@ public class BridgeSettings { public void setButtonsleep(Integer buttonsleep) { this.buttonsleep = buttonsleep; } + public boolean isRestart() { + return restart; + } + public void setRestart(boolean restart) { + this.restart = restart; + } + public boolean isStop() { + return stop; + } + public void setStop(boolean stop) { + this.stop = stop; + } + public String getConfigfile() { + return configfile; + } + public void setConfigfile(String configfile) { + this.configfile = configfile; + } public Boolean isValidVera() { List devicesList = this.veraaddress.getDevices(); if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS)) diff --git a/src/main/java/com/bwssystems/HABridge/Configuration.java b/src/main/java/com/bwssystems/HABridge/Configuration.java index 26b3bac..5c0c0bd 100644 --- a/src/main/java/com/bwssystems/HABridge/Configuration.java +++ b/src/main/java/com/bwssystems/HABridge/Configuration.java @@ -12,4 +12,7 @@ public class Configuration { public final static String DEFAULT_PWD = ""; public final static String DFAULT_WEB_PORT = "8080"; public final static String DFAULT_BUTTON_SLEEP = "100"; + public static final int UPNP_DISCOVERY_PORT = 1900; + public static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250"; + public static final String CONFIG_FILE = "data/habridge.config"; } diff --git a/src/main/java/com/bwssystems/HABridge/HABridge.java b/src/main/java/com/bwssystems/HABridge/HABridge.java index d1c9b50..6818ce3 100644 --- a/src/main/java/com/bwssystems/HABridge/HABridge.java +++ b/src/main/java/com/bwssystems/HABridge/HABridge.java @@ -51,102 +51,114 @@ public class HABridge { theVersion = new Version(); - log.info("HA Bridge (v" + theVersion.getVersion() + ") starting setup...."); + log.info("HA Bridge (v" + theVersion.getVersion() + ") starting...."); bridgeSettings = new BridgeSettings(); - bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT)); - bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", Configuration.DEFAULT_ADDRESS)); - if(bridgeSettings.getUpnpConfigAddress().equalsIgnoreCase(Configuration.DEFAULT_ADDRESS)) { - 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."); + bridgeSettings.setRestart(false); + bridgeSettings.setStop(false); + while(!bridgeSettings.isStop()) { + bridgeSettings.setConfigfile(System.getProperty("config.file", Configuration.CONFIG_FILE)); + bridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DFAULT_WEB_PORT)); + bridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address", Configuration.DEFAULT_ADDRESS)); + if(bridgeSettings.getUpnpConfigAddress().equalsIgnoreCase(Configuration.DEFAULT_ADDRESS)) { + try { + log.info("HA Bridge (v" + theVersion.getVersion() + ") 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("HA Bridge (v" + theVersion.getVersion() + ") 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; } - } catch (SocketException e) { - log.error("Cannot get ip address of this host, Exiting with message: " + e.getMessage(), e); - return; - } + + bridgeSettings.setUpnpConfigAddress(addressString); + } - bridgeSettings.setUpnpConfigAddress(addressString); - } + bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY)); + bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT)); + IpList theVeraList; + + try { + theVeraList = new Gson().fromJson(System.getProperty("vera.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class); + } catch (Exception e) { + try { + theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("vera.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class); + } catch (Exception et) { + log.error("Cannot parse vera.address, Exiting with message: " + e.getMessage(), e); + return; + } + } + bridgeSettings.setVeraAddress(theVeraList); + IpList theHarmonyList; + + try { + theHarmonyList = new Gson().fromJson(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class); + } catch (Exception e) { + try { + theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("harmony.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class); + } catch (Exception et) { + log.error("Cannot parse harmony.address, Exiting with message: " + e.getMessage(), e); + return; + } + } + bridgeSettings.setHarmonyAddress(theHarmonyList); + bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_USER)); + bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_PWD)); + bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true"))); + bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false"))); + bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false"))); + bridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DFAULT_BUTTON_SLEEP))); + bridgeSettings.setNestuser(System.getProperty("nest.user", Configuration.DEFAULT_USER)); + bridgeSettings.setNestpwd(System.getProperty("nest.pwd", Configuration.DEFAULT_PWD)); - bridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db", Configuration.DEVICE_DB_DIRECTORY)); - bridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT)); - IpList theVeraList; + log.info("HA Bridge (v" + theVersion.getVersion() + ") initializing...."); + // sparkjava config directive to set ip address for the web server to listen on + // ipAddress("0.0.0.0"); // not used + // sparkjava config directive to set port for the web server to listen on + port(Integer.valueOf(bridgeSettings.getServerPort())); + // sparkjava config directive to set html static file location for Jetty + staticFileLocation("/public"); + //setup the harmony connection if available + harmonyHome = new HarmonyHome(bridgeSettings); + //setup the nest connection if available + nestHome = new NestHome(bridgeSettings); + // setup the class to handle the resource setup rest api + theResources = new DeviceResource(bridgeSettings, theVersion, harmonyHome, nestHome); + // setup the class to handle the hue emulator rest api + theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), harmonyHome, nestHome); + theHueMulator.setupServer(); + // setup the class to handle the upnp response rest api + theSettingResponder = new UpnpSettingsResource(bridgeSettings); + 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); + if(theUpnpListener.startListening()) + log.info("HA Bridge (v" + theVersion.getVersion() + ") reinitialization requessted...."); - try { - theVeraList = new Gson().fromJson(System.getProperty("vera.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class); - } catch (Exception e) { - try { - theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("vera.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class); - } catch (Exception et) { - log.error("Cannot parse vera.address, Exiting with message: " + e.getMessage(), e); - return; - } + bridgeSettings.setRestart(false); + stop(); } - bridgeSettings.setVeraAddress(theVeraList); - IpList theHarmonyList; - - try { - theHarmonyList = new Gson().fromJson(System.getProperty("harmony.address", Configuration.DEFAULT_HARMONY_ADDRESS_LIST), IpList.class); - } catch (Exception e) { - try { - theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + System.getProperty("harmony.address", Configuration.DEFAULT_ADDRESS) + "}]}", IpList.class); - } catch (Exception et) { - log.error("Cannot parse harmony.address, Exiting with message: " + e.getMessage(), e); - return; - } - } - bridgeSettings.setHarmonyAddress(theHarmonyList); - bridgeSettings.setHarmonyUser(System.getProperty("harmony.user", Configuration.DEFAULT_USER)); - bridgeSettings.setHarmonyPwd(System.getProperty("harmony.pwd", Configuration.DEFAULT_PWD)); - bridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true"))); - bridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false"))); - bridgeSettings.setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false"))); - bridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DFAULT_BUTTON_SLEEP))); - bridgeSettings.setNestuser(System.getProperty("nest.user", Configuration.DEFAULT_USER)); - bridgeSettings.setNestpwd(System.getProperty("nest.pwd", Configuration.DEFAULT_PWD)); - - // sparkjava config directive to set ip address for the web server to listen on - // ipAddress("0.0.0.0"); // not used - // sparkjava config directive to set port for the web server to listen on - port(Integer.valueOf(bridgeSettings.getServerPort())); - // sparkjava config directive to set html static file location for Jetty - staticFileLocation("/public"); - //setup the harmony connection if available - harmonyHome = new HarmonyHome(bridgeSettings); - //setup the nest connection if available - nestHome = new NestHome(bridgeSettings); - // setup the class to handle the resource setup rest api - theResources = new DeviceResource(bridgeSettings, theVersion, harmonyHome, nestHome); - // setup the class to handle the hue emulator rest api - theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), harmonyHome, nestHome); - theHueMulator.setupServer(); - // setup the class to handle the upnp response rest api - theSettingResponder = new UpnpSettingsResource(bridgeSettings); - 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); - theUpnpListener.startListening(); + log.info("HA Bridge (v" + theVersion.getVersion() + ") exiting...."); + System.exit(0); } } diff --git a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java index 1fe6c8b..f350be4 100644 --- a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java +++ b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java @@ -23,9 +23,7 @@ import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.dao.DeviceRepository; import com.bwssystems.NestBridge.NestHome; import com.bwssystems.harmony.HarmonyHome; -import com.bwssystems.luupRequests.Sdata; import com.bwssystems.vera.VeraHome; -import com.bwssystems.vera.VeraInfo; import com.google.gson.Gson; /** diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index a68dde6..ba5c81f 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -1,6 +1,7 @@ package com.bwssystems.HABridge.hue; import com.bwssystems.HABridge.BridgeSettings; +import com.bwssystems.HABridge.Configuration; import com.bwssystems.HABridge.JsonTransformer; import com.bwssystems.HABridge.api.UserCreateRequest; import com.bwssystems.HABridge.api.hue.DeviceResponse; @@ -45,6 +46,7 @@ import java.math.BigDecimal; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; +import java.net.MulticastSocket; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -439,6 +441,38 @@ public class HueMulator { return responseString; }); + + // http://ip_address:port/api/control/restart CORS request + options(HUE_CONTEXT + "/control/restart", "application/json", (request, response) -> { + response.status(HttpStatus.SC_OK); + 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"); + return ""; + }); + // http://ip_address:port/api//control/restart sets the parameter restart the server + put(HUE_CONTEXT + "/control/restart", "application/json", (request, response) -> { + bridgeSettings.setRestart(true); + pingListener(); + return "{\"control\":\"restarting\"}"; + }); + + // http://ip_address:port/api/control/stop CORS request + options(HUE_CONTEXT + "/control/stop", "application/json", (request, response) -> { + response.status(HttpStatus.SC_OK); + 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"); + return ""; + }); + // http://ip_address:port/api//control/stop sets the parameter stop the server + put(HUE_CONTEXT + "/control/stop", "application/json", (request, response) -> { + bridgeSettings.setStop(true); + pingListener(); + return "{\"control\":\"stopping\"}"; + }); } /* light weight templating here, was going to use free marker but it was a bit too @@ -510,4 +544,23 @@ public class HueMulator { } return false; } + + protected void pingListener() { + try { + byte[] buf = new byte[256]; + String testData = "M-SEARCH * HTTP/1.1\nHOST: " + Configuration.UPNP_MULTICAST_ADDRESS + ":" + Configuration.UPNP_DISCOVERY_PORT + "ST: urn:schemas-upnp-org:device:CloudProxy:1\nMAN: \"ssdp:discover\"\nMX: 3"; + buf = testData.getBytes(); + MulticastSocket socket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT); + + InetAddress group = InetAddress.getByName(Configuration.UPNP_MULTICAST_ADDRESS); + DatagramPacket packet; + packet = new DatagramPacket(buf, buf.length, group, Configuration.UPNP_DISCOVERY_PORT); + socket.send(packet); + + socket.close(); + } + catch (IOException e) { + log.warn("Error pinging listener.", e); + } + } } diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java index 531fc6e..7567db3 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpListener.java @@ -4,6 +4,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.BridgeSettings; +import com.bwssystems.HABridge.Configuration; import java.io.IOException; import java.net.*; @@ -14,8 +15,6 @@ import org.apache.http.conn.util.*; public class UpnpListener { private Logger log = LoggerFactory.getLogger(UpnpListener.class); - private static final int UPNP_DISCOVERY_PORT = 1900; - private static final String UPNP_MULTICAST_ADDRESS = "239.255.255.250"; private int upnpResponsePort; @@ -26,6 +25,7 @@ public class UpnpListener { private boolean strict; private boolean traceupnp; + private BridgeSettings bridgeSettings; public UpnpListener(BridgeSettings theSettings) { super(); @@ -34,14 +34,15 @@ public class UpnpListener { responseAddress = theSettings.getUpnpConfigAddress(); strict = theSettings.isUpnpStrict(); traceupnp = theSettings.isTraceupnp(); + bridgeSettings = theSettings; } - public void startListening(){ + public boolean startListening(){ log.info("UPNP Discovery Listener starting...."); try (DatagramSocket responseSocket = new DatagramSocket(upnpResponsePort); - MulticastSocket upnpMulticastSocket = new MulticastSocket(UPNP_DISCOVERY_PORT);) { - InetSocketAddress socketAddress = new InetSocketAddress(UPNP_MULTICAST_ADDRESS, UPNP_DISCOVERY_PORT); + MulticastSocket upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);) { + InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT); Enumeration ifs = NetworkInterface.getNetworkInterfaces(); while (ifs.hasMoreElements()) { @@ -71,22 +72,29 @@ public class UpnpListener { } log.info("UPNP Discovery Listener running and ready...."); - - while(true){ //trigger shutdown here + boolean loopControl = true; + while(loopControl){ //trigger shutdown here byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); upnpMulticastSocket.receive(packet); if(isSSDPDiscovery(packet)){ sendUpnpResponse(responseSocket, packet.getAddress(), packet.getPort()); } + if(bridgeSettings.isRestart() || bridgeSettings.isStop()) + loopControl = false; } - + upnpMulticastSocket.close(); + responseSocket.close(); } catch (IOException e) { log.error("UpnpListener encountered an error opening sockets. Shutting down", e); - } - log.info("UPNP Discovery Listener Stopped"); - + if(bridgeSettings.isRestart()) + log.info("UPNP Discovery Listener - ended, restart found"); + if(bridgeSettings.isStop()) + log.info("UPNP Discovery Listener - ended, stop found"); + if(!bridgeSettings.isStop()&& !bridgeSettings.isRestart()) + log.info("UPNP Discovery Listener - ended, error found"); + return bridgeSettings.isRestart(); } /**