diff --git a/pom.xml b/pom.xml index 623a4ea..93df6ba 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 1.3.8a + 1.3.8b jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index f931859..7a63c3c 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -1,7 +1,29 @@ package com.bwssystems.HABridge; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Enumeration; import java.util.List; +import org.apache.http.conn.util.InetAddressUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; + public class BridgeSettings { private String upnpconfigaddress; private String serverport; @@ -17,11 +39,24 @@ public class BridgeSettings { private boolean devmode; private String nestuser; private String nestpwd; + private boolean veraconfigured; + private boolean harmonyconfigured; private boolean nestconfigured; private String configfile; private boolean restart; private boolean stop; + public BridgeSettings() { + super(); + this.upnpstrict = true; + this.devmode = false; + this.traceupnp = false; + this.restart = false; + this.stop = false; + this.nestconfigured = false; + this.veraconfigured = false; + this.harmonyconfigured = false; + } public String getUpnpConfigAddress() { return upnpconfigaddress; } @@ -100,6 +135,18 @@ public class BridgeSettings { public void setNestpwd(String nestpwd) { this.nestpwd = nestpwd; } + public boolean isVeraconfigured() { + return veraconfigured; + } + public void setVeraconfigured(boolean veraconfigured) { + this.veraconfigured = veraconfigured; + } + public boolean isHarmonyconfigured() { + return harmonyconfigured; + } + public void setHarmonyconfigured(boolean harmonyconfigured) { + this.harmonyconfigured = harmonyconfigured; + } public boolean isNestConfigured() { return nestconfigured; } @@ -131,16 +178,19 @@ public class BridgeSettings { this.configfile = configfile; } public Boolean isValidVera() { + if(this.veraaddress == null) + return false; List devicesList = this.veraaddress.getDevices(); if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS)) return false; return true; } public Boolean isValidHarmony() { + if(this.harmonyaddress == null) + return false; List devicesList = this.harmonyaddress.getDevices(); if(devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS)) - return false; - if(this.harmonypwd == null || this.harmonypwd == "") + return false; if(this.harmonypwd == null || this.harmonypwd == "") return false; if(this.harmonyuser == null || this.harmonyuser == "") return false; @@ -153,4 +203,269 @@ public class BridgeSettings { return false; return true; } + + public void buildSettings() { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + InetAddress address = null; + String addressString = null; + String theVeraAddress = null; + String theHarmonyAddress = null; + String configFileProperty = System.getProperty("config.file"); + if(configFileProperty == null) { + Path filePath = Paths.get(Configuration.CONFIG_FILE); + if(Files.exists(filePath) && Files.isReadable(filePath)) + configFileProperty = Configuration.CONFIG_FILE; + } + if(configFileProperty != null) + { + log.info("reading from config file: " + configFileProperty); + setConfigfile(configFileProperty); + _loadConfig(); + } + else + { + log.info("reading from system properties"); + setConfigfile(Configuration.CONFIG_FILE); + setServerPort(System.getProperty("server.port")); + setUpnpConfigAddress(System.getProperty("upnp.config.address")); + setUpnpDeviceDb(System.getProperty("upnp.device.db")); + setUpnpResponsePort(System.getProperty("upnp.response.port")); + + theVeraAddress = System.getProperty("vera.address"); + IpList theVeraList = null; + if(theVeraAddress != null) { + try { + theVeraList = new Gson().fromJson(theVeraAddress, IpList.class); + } catch (Exception e) { + try { + theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + theVeraAddress + "}]}", IpList.class); + } catch (Exception et) { + log.error("Cannot parse vera.address, not set with message: " + e.getMessage(), e); + theVeraList = null; + } + } + } + setVeraAddress(theVeraList); + + theHarmonyAddress = System.getProperty("harmony.address"); + IpList theHarmonyList = null; + if(theHarmonyAddress != null) { + try { + theHarmonyList = new Gson().fromJson(theHarmonyAddress, IpList.class); + } catch (Exception e) { + try { + theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + theHarmonyAddress + "}]}", IpList.class); + } catch (Exception et) { + log.error("Cannot parse harmony.address, not set with message: " + e.getMessage(), e); + theHarmonyList = null; + } + } + } + setHarmonyAddress(theHarmonyList); + setHarmonyUser(System.getProperty("harmony.user")); + setHarmonyPwd(System.getProperty("harmony.pwd")); + setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true"))); + setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false"))); + setDevMode(Boolean.parseBoolean(System.getProperty("dev.mode", "false"))); + setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DFAULT_BUTTON_SLEEP))); + setNestuser(System.getProperty("nest.user")); + setNestpwd(System.getProperty("nest.pwd")); + } + + if(getUpnpConfigAddress() == null) { + 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; + } + + setUpnpConfigAddress(addressString); + } + + if(this.getUpnpResponsePort() == null) + this.setUpnpResponsePort(Configuration.UPNP_RESPONSE_PORT); + + if(this.getServerPort() == null) + this.setServerPort(Configuration.DFAULT_WEB_PORT); + + if(this.getUpnpDeviceDb() == null) + this.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY); + + if(this.getButtonsleep() <= 0) + this.setButtonsleep(Integer.parseInt(Configuration.DFAULT_BUTTON_SLEEP)); + } + + private void _loadConfig() { + Path configPath = Paths.get(getConfigfile()); + _loadConfig(configPath); + } + + private void _loadConfig(Path aPath) { + String jsonContent = configReader(aPath); + BridgeSettings aBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettings.class); + this.setButtonsleep(aBridgeSettings.getButtonsleep()); + this.setUpnpConfigAddress(aBridgeSettings.getUpnpConfigAddress()); + this.setServerPort(aBridgeSettings.getServerPort()); + this.setUpnpResponsePort(aBridgeSettings.getUpnpResponsePort()); + this.setUpnpDeviceDb(aBridgeSettings.getUpnpDeviceDb()); + this.setVeraAddress(aBridgeSettings.getVeraAddress()); + this.setHarmonyAddress(aBridgeSettings.getHarmonyAddress()); + this.setHarmonyUser(aBridgeSettings.getHarmonyUser()); + this.setHarmonyPwd(aBridgeSettings.getHarmonyPwd()); + this.setUpnpStrict(aBridgeSettings.isUpnpStrict()); + this.setTraceupnp(aBridgeSettings.isTraceupnp()); + this.setDevMode(aBridgeSettings.isDevMode()); + this.setNestuser(aBridgeSettings.getNestuser()); + this.setNestpwd(aBridgeSettings.getNestpwd()); + this.setVeraconfigured(aBridgeSettings.isVeraconfigured()); + this.setHarmonyconfigured(aBridgeSettings.isHarmonyconfigured()); + this.setNestConfigured(aBridgeSettings.isNestConfigured()); + } + + public void save() { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + Path configPath = Paths.get(getConfigfile()); + JsonTransformer aRenderer = new JsonTransformer(); + String jsonValue = aRenderer.render(this); + configWriter(jsonValue, configPath); + log.debug("Save HA Bridge settings."); + } + + + private void configWriter(String content, Path filePath) { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + if(Files.exists(filePath) && !Files.isWritable(filePath)){ + log.error("Error file is not writable: " + filePath); + return; + } + + if(Files.notExists(filePath.getParent())) { + try { + Files.createDirectories(filePath.getParent()); + } catch (IOException e) { + log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e); + } + } + + try { + Path target = null; + if(Files.exists(filePath)) { + target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "device.db.old"); + Files.move(filePath, target); + } + Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE); + if(target != null) + Files.delete(target); + } catch (IOException e) { + log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e); + } + } + + private String configReader(Path filePath) { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + + String content = null; + if(Files.notExists(filePath) || !Files.isReadable(filePath)){ + log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing..."); + return null; + } + + + try { + content = new String(Files.readAllBytes(filePath)); + } catch (IOException e) { + log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e); + } + + return content; + } + + public String backupConfig(String aFilename) { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + Path configPath = Paths.get(getConfigfile()); + if(aFilename == null || aFilename.equalsIgnoreCase("")) { + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + aFilename = "habridge.config-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk"; + } + else + aFilename = aFilename + ".bk"; + try { + Files.copy(configPath, FileSystems.getDefault().getPath(configPath.getParent().toString(), aFilename), StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + log.error("Could not backup to file: " + aFilename + " message: " + e.getMessage(), e); + } + log.debug("Backup config: " + aFilename); + return aFilename; + } + + public String deleteConfigBackup(String aFilename) { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + Path configPath = Paths.get(getConfigfile()); + log.debug("Delete backup config: " + aFilename); + try { + Files.delete(FileSystems.getDefault().getPath(configPath.getParent().toString(), aFilename)); + } catch (IOException e) { + log.error("Could not delete file: " + aFilename + " message: " + e.getMessage(), e); + } + return aFilename; + } + + public String restoreConfigBackup(String aFilename) { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + Path configPath = Paths.get(getConfigfile()); + log.debug("Restore backup config: " + aFilename); + try { + Path target = null; + if(Files.exists(configPath)) { + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + target = FileSystems.getDefault().getPath(configPath.getParent().toString(), "habridge.config-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk"); + Files.move(configPath, target); + } + Files.copy(FileSystems.getDefault().getPath(configPath.getParent().toString(), aFilename), configPath, StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + log.error("Error restoring the file: " + aFilename + " message: " + e.getMessage(), e); + return null; + } + _loadConfig(configPath); + return aFilename; + } + + public List getConfigBackups() { + Logger log = LoggerFactory.getLogger(BridgeSettings.class); + Path configPath = Paths.get(getConfigfile()); + List theFilenames = new ArrayList(); + Path dir = configPath.getParent(); + try (DirectoryStream stream = + Files.newDirectoryStream(dir, "*.{bk}")) { + for (Path entry: stream) { + theFilenames.add(entry.getFileName().toString()); + } + } catch (IOException x) { + // IOException can never be thrown by the iteration. + // In this snippet, it can // only be thrown by newDirectoryStream. + log.error("Issue getting directory listing for config backups - " + x.getMessage()); + } + return theFilenames; + } } diff --git a/src/main/java/com/bwssystems/HABridge/HABridge.java b/src/main/java/com/bwssystems/HABridge/HABridge.java index 6818ce3..210f808 100644 --- a/src/main/java/com/bwssystems/HABridge/HABridge.java +++ b/src/main/java/com/bwssystems/HABridge/HABridge.java @@ -2,12 +2,6 @@ package com.bwssystems.HABridge; import static spark.Spark.*; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; - -import org.apache.http.conn.util.InetAddressUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,7 +11,6 @@ import com.bwssystems.HABridge.upnp.UpnpListener; import com.bwssystems.HABridge.upnp.UpnpSettingsResource; import com.bwssystems.NestBridge.NestHome; import com.bwssystems.harmony.HarmonyHome; -import com.google.gson.Gson; public class HABridge { @@ -44,8 +37,6 @@ public class HABridge { HueMulator theHueMulator; UpnpSettingsResource theSettingResponder; UpnpListener theUpnpListener; - InetAddress address = null; - String addressString = null; BridgeSettings bridgeSettings; Version theVersion; @@ -57,77 +48,7 @@ public class HABridge { 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; - } - - 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.buildSettings(); 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 diff --git a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java index dbcfb41..a613eb2 100644 --- a/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java +++ b/src/main/java/com/bwssystems/HABridge/upnp/UpnpSettingsResource.java @@ -53,6 +53,8 @@ public class UpnpSettingsResource { this.theSettings.setUpnpResponsePort(theBridgeSettings.getUpnpResponsePort()); this.theSettings.setUpnpStrict(theBridgeSettings.isUpnpStrict()); this.theSettings.setVeraAddress(theBridgeSettings.getVeraAddress()); + this.theSettings.setVeraconfigured(theBridgeSettings.isValidVera()); + this.theSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony()); this.theSettings.setNestConfigured(theBridgeSettings.isValidNest()); } diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 18feb09..ccbd851 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -79,31 +79,18 @@ app.service('bridgeService', function ($http, $window, ngToast) { } this.updateShowVera = function () { - if(this.aContainsB(self.state.settings.veraaddress.devices[0].ip, "1.1.1.1") || self.state.settings.veraaddress == "" || self.state.settings.veraaddress == null) - this.state.showVera = false; - else - this.state.showVera = true; + this.state.showVera = self.state.settings.veraconfigured; return; } this.updateShowNest = function () { - if(self.state.settings.nestconfigured == true) - this.state.showNest = true; - else - this.state.showNest = false; + this.state.showNest = self.state.settings.nestconfigured; return; } this.updateShowHarmony = function () { - if(self.state.settings.harmonyaddress.devices) { - if(this.aContainsB(self.state.settings.harmonyaddress.devices[0].ip, "1.1.1.1") || self.state.settings.harmonyaddress == "" || self.state.settings.harmonyaddress == null) - this.state.showHarmony = false; - else - this.state.showHarmony = true; - } - else - this.state.showHarmony = false; - return; + this.state.showHarmony = self.state.settings.harmonyconfigured; + return; }