From d15a1c58d0f26959901e95e43b294064ce9ff883 Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 10 Mar 2017 16:22:48 -0600 Subject: [PATCH 1/5] update readme for harmony webhook addition --- README.md | 14 +++----------- pom.xml | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ae82f42..298aaf0 100644 --- a/README.md +++ b/README.md @@ -289,11 +289,7 @@ The upnp response port that will be used. The default is 50000. #### Vera Names and IP Addresses Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure. #### Harmony Names and IP Addresses -Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure. -#### Harmony Username -deprecated -#### Harmony Password -deprecated +Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure. Also, an option of webhook can be called when the activity changes on the harmony hub that will send an HTTP GET call to the the address of your choosing. This can contain the replacement variables of ${activity.id} and/or ${activity.label}. Example : http://192.168.0.1/activity/${activity.id}/${activity.label} OR http://hook?a=${activity.label} #### Hue Names and IP Addresses Provide IP Addresses of your Hue Bridges that you want to proxy through the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will passthru the call it receives to the target Hue and device you configure. @@ -425,13 +421,9 @@ To configure this type of manual add, you will need to select the Device type of In the URL areas, the format of the execution is just providing what command line you would like to run, or using the multiple call item construct described above. ``` -[{"item":"C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"}, -{"item":"notepad.exe","type":"cmdDevice"}] - -OR - -[{"item":"exec://notepad.exe","type":"cmdDevice"}] +[{"item":"exec://C:\\Users\\John\\Documents\\Applications\\putty.exe 192.168.1.1","type":"cmdDevice"},{"item":"exec://notepad.exe","type":"cmdDevice"}] +[{"item":"/home/pi/scripts/dothisscript.sh","type":"cmdDevice"}] ``` #### Value Passing Controls There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.decimal_percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}". diff --git a/pom.xml b/pom.xml index 638a919..be984ee 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 4.2.1 + 4.2.1a jar HA Bridge From fb24e9d1a35928d23ea97232f04471365688e286 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 13 Mar 2017 16:37:42 -0500 Subject: [PATCH 2/5] Updated whitelist handling to save users when created. Also, added default test user for habridge UI. Made change state to be validated against whitelist. --- README.md | 62 ++--------- pom.xml | 2 +- .../bwssystems/HABridge/BridgeSettings.java | 17 +++ .../HABridge/BridgeSettingsDescriptor.java | 82 ++++++++++++++ .../com/bwssystems/HABridge/HABridge.java | 2 +- .../bwssystems/HABridge/SystemControl.java | 7 ++ .../bwssystems/HABridge/hue/HueMulator.java | 103 +++++++----------- src/main/resources/public/scripts/app.js | 14 ++- 8 files changed, 167 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 298aaf0..d95a6ec 100644 --- a/README.md +++ b/README.md @@ -59,23 +59,23 @@ ATTENTION: This requires JDK 1.8 to run ATTENTION: Due to port 80 being the default, Linux restricts this to super user. Use the instructions below. ``` -java -jar ha-bridge-4.2.1.jar +java -jar ha-bridge-4.3.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 -Create the directory and make sure that ha-bridge-4.2.1.jar is in your /home/pi/habridge directory. +Create the directory and make sure that ha-bridge-4.3.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/v4.2.1/ha-bridge-4.2.1.jar +pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.3.0/ha-bridge-4.3.0.jar ``` -Create the directory and make sure that ha-bridge-4.2.1.jar is in your /home/pi/habridge directory. +Create the directory and make sure that ha-bridge-4.3.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/v4.2.1/ha-bridge-4.2.1.jar +pi@raspberrypi:~/habridge $ wget https://github.com/bwssytems/ha-bridge/releases/download/v4.3.0/ha-bridge-4.3.0.jar ``` #### System Control Setup on a pi (preferred) For next gen Linux systems (this includes the Raspberry Pi), here is a systemctl unit file that you can install. Here is a link on how to do this: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units @@ -95,7 +95,7 @@ After=network.target [Service] Type=simple -ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.2.1.jar +ExecStart=/usr/bin/java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.3.0.jar [Install] WantedBy=multi-user.target @@ -130,7 +130,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 -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.2.1.jar > /home/pi/habridge/habridge-log.txt 2>&1 & +nohup java -jar -Dconfig.file=/home/pi/habridge/data/habridge.config /home/pi/habridge/ha-bridge-4.3.0.jar > /home/pi/habridge/habridge-log.txt 2>&1 & chmod 777 /home/pi/habridge/habridge-log.txt ``` @@ -175,54 +175,6 @@ Added the following lines to my Apache config file “000-default” ``` -service apache2 restart -### lighthttpd Example -``` -server.modules += ( "mod_proxy" ) -proxy.server = ( - "/api" => - ( - ( "host" => "127.0.0.1", - "port" => "8080" - ) - ) -) -``` -### nginx Example -``` -location /api/ { - proxy_pass http://127.0.0.1:8080/api; -} -``` -## Run ha-bridge alongside web server already on port 80 -These examples will help you proxy your current webserver requests to the ha-bridge running on a different port, such as 8080. -### Apache Example -Reverse proxy with Apache on Ubuntu linux: - -a2enmod proxy -a2enmod proxy_http -a2enmod headers - -Added the following lines to my Apache config file “000-default” - -``` - - ProxyPass /api http://localhost:8080/api nocanon - ProxyPassReverse /api http://localhost:8080/api - ProxyRequests Off - AllowEncodedSlashes NoDecode - - # Local reverse proxy authorization override - # Most unix distribution deny proxy by default (ie /etc/apache2/mods-enabled/proxy.conf in Ubuntu) - - Order deny,allow - Allow from all - - -….. (the rest of the VirtualHost config section) ….. - -``` - service apache2 restart ### lighthttpd Example ``` diff --git a/pom.xml b/pom.xml index be984ee..3d94517 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 4.2.1a + 4.2.1b jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java index 13d84d4..cc5e6e8 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettings.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettings.java @@ -154,6 +154,8 @@ public class BridgeSettings extends BackupHandler { if(serverIpOverride != null) theBridgeSettings.setWebaddress(serverIpOverride); setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-"); + + setupInternalTestUser(); } public void loadConfig() { @@ -188,6 +190,16 @@ public class BridgeSettings extends BackupHandler { } + public void updateConfigFile() { + log.debug("Save HA Bridge settings."); + Path configPath = Paths.get(theBridgeSettings.getConfigfile()); + JsonTransformer aRenderer = new JsonTransformer(); + String jsonValue = aRenderer.render(theBridgeSettings); + configWriter(jsonValue, configPath); + _loadConfig(configPath); + } + + private void configWriter(String content, Path filePath) { if(Files.exists(filePath) && !Files.isWritable(filePath)){ log.error("Error file is not writable: " + filePath); @@ -279,4 +291,9 @@ public class BridgeSettings extends BackupHandler { } return addressString; } + private void setupInternalTestUser() { + theBridgeSettings.setupInternalTestUser(); + if(theBridgeSettings.isSettingsChanged()) + this.updateConfigFile(); + } } diff --git a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java index 556d85f..8836976 100644 --- a/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/BridgeSettingsDescriptor.java @@ -1,12 +1,21 @@ package com.bwssystems.HABridge; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.UUID; import com.bwssystems.HABridge.api.hue.HueConstants; +import com.bwssystems.HABridge.api.hue.HueError; +import com.bwssystems.HABridge.api.hue.HueErrorResponse; import com.bwssystems.HABridge.api.hue.WhitelistEntry; public class BridgeSettingsDescriptor { + private static final String DEFAULT_INTERNAL_USER = "thehabridgeuser"; + private static final String DEFAULT_USER_DESCRIPTION = "default_test_user"; private String upnpconfigaddress; private Integer serverport; private Integer upnpresponseport; @@ -338,4 +347,77 @@ public class BridgeSettingsDescriptor { public Boolean isValidLifx() { return this.isLifxconfigured(); } + + public HueError[] validateWhitelistUser(String aUser, String userDescription, boolean strict) { + String validUser = null; + boolean found = false; + if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null") + && !aUser.equalsIgnoreCase("")) { + if (whitelist != null) { + Set theUserIds = whitelist.keySet(); + Iterator userIterator = theUserIds.iterator(); + while (userIterator.hasNext()) { + validUser = userIterator.next(); + if (validUser.equals(aUser)) + found = true; + } + } + } + + if(!found && !strict) { + newWhitelistUser(aUser, userDescription); + + found = true; + } + + if (!found) { + return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors(); + } + + return null; + } + + public void newWhitelistUser(String aUser, String userDescription) { + if (whitelist == null) { + whitelist = new HashMap<>(); + } + if(userDescription == null) + userDescription = "auto insert user"; + + whitelist.put(aUser, WhitelistEntry.createEntry(userDescription)); + setSettingsChanged(true); + } + + public String createWhitelistUser(String userDescription) { + String aUser = getNewUserID(); + newWhitelistUser(aUser, userDescription); + return aUser; + } + + private String getNewUserID() { + UUID uid = UUID.randomUUID(); + StringTokenizer st = new StringTokenizer(uid.toString(), "-"); + String newUser = ""; + while (st.hasMoreTokens()) { + newUser = newUser + st.nextToken(); + } + + return newUser; + } + + public String getInternalTestUser() { + return DEFAULT_INTERNAL_USER; + } + + public void setupInternalTestUser() { + boolean found = false; + for (String key : whitelist.keySet()) { + if(key.equals(DEFAULT_INTERNAL_USER)) + found = true; + } + + if(!found) { + newWhitelistUser(DEFAULT_INTERNAL_USER, DEFAULT_USER_DESCRIPTION); + } + } } diff --git a/src/main/java/com/bwssystems/HABridge/HABridge.java b/src/main/java/com/bwssystems/HABridge/HABridge.java index 81cb921..f0c86a5 100644 --- a/src/main/java/com/bwssystems/HABridge/HABridge.java +++ b/src/main/java/com/bwssystems/HABridge/HABridge.java @@ -72,7 +72,7 @@ public class HABridge { theSettingResponder = new UpnpSettingsResource(bridgeSettings.getBridgeSettingsDescriptor()); theSettingResponder.setupServer(); // setup the class to handle the hue emulator rest api - theHueMulator = new HueMulator(bridgeSettings.getBridgeSettingsDescriptor(), theResources.getDeviceRepository(), homeManager); + theHueMulator = new HueMulator(bridgeSettings, theResources.getDeviceRepository(), homeManager); theHueMulator.setupServer(); // wait for the sparkjava initialization of the rest api classes to be complete awaitInitialization(); diff --git a/src/main/java/com/bwssystems/HABridge/SystemControl.java b/src/main/java/com/bwssystems/HABridge/SystemControl.java index c1d8934..eaa1db6 100644 --- a/src/main/java/com/bwssystems/HABridge/SystemControl.java +++ b/src/main/java/com/bwssystems/HABridge/SystemControl.java @@ -62,6 +62,13 @@ public class SystemControl { return "{\"version\":\"" + version.getVersion() + "\"}"; }); + // http://ip_address:port/system/habridge/testuser gets the valid test user for calling the api + get (SYSTEM_CONTEXT + "/habridge/testuser", "application/json", (request, response) -> { + log.debug("Get HA Bridge testuser: " + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser()); + response.status(HttpStatus.SC_OK); + return "{\"user\":\"" + bridgeSettings.getBridgeSettingsDescriptor().getInternalTestUser() + "\"}"; + }); + // http://ip_address:port/system/logmsgs gets the log messages for the bridge get (SYSTEM_CONTEXT + "/logmsgs", "application/json", (request, response) -> { log.debug("Get logmsgs."); diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index dca0dfe..fa1b439 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -1,5 +1,6 @@ package com.bwssystems.HABridge.hue; +import com.bwssystems.HABridge.BridgeSettings; import com.bwssystems.HABridge.BridgeSettingsDescriptor; import com.bwssystems.HABridge.DeviceMapTypes; import com.bwssystems.HABridge.HomeManager; @@ -14,7 +15,6 @@ import com.bwssystems.HABridge.api.hue.HueError; import com.bwssystems.HABridge.api.hue.HueErrorResponse; import com.bwssystems.HABridge.api.hue.HuePublicConfig; import com.bwssystems.HABridge.api.hue.StateChangeBody; -import com.bwssystems.HABridge.api.hue.WhitelistEntry; import com.bwssystems.HABridge.dao.*; import com.bwssystems.HABridge.plugins.hue.HueHome; import com.bwssystems.HABridge.util.JsonTransformer; @@ -33,12 +33,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.UUID; /** * Based on Armzilla's HueMulator - a Philips Hue emulator using sparkjava rest server @@ -52,13 +48,15 @@ public class HueMulator { private HomeManager homeManager; private HueHome myHueHome; private BridgeSettingsDescriptor bridgeSettings; + private BridgeSettings bridgeSettingMaster; private Gson aGsonHandler; private DeviceMapTypes validMapTypes; - public HueMulator(BridgeSettingsDescriptor theBridgeSettings, DeviceRepository aDeviceRepository, HomeManager aHomeManager) { + public HueMulator(BridgeSettings bridgeMaster, DeviceRepository aDeviceRepository, HomeManager aHomeManager) { repository = aDeviceRepository; validMapTypes = new DeviceMapTypes(); - bridgeSettings = theBridgeSettings; + bridgeSettingMaster = bridgeMaster; + bridgeSettings = bridgeSettingMaster.getBridgeSettingsDescriptor(); homeManager= aHomeManager; myHueHome = (HueHome) homeManager.findHome(DeviceMapTypes.HUE_DEVICE[DeviceMapTypes.typeIndex]); aGsonHandler = new GsonBuilder().create(); @@ -556,50 +554,6 @@ public class HueMulator { return responseString; } - private String getNewUserID() { - UUID uid = UUID.randomUUID(); - StringTokenizer st = new StringTokenizer(uid.toString(), "-"); - String newUser = ""; - while (st.hasMoreTokens()) { - newUser = newUser + st.nextToken(); - } - - return newUser; - } - private HueError[] validateWhitelistUser(String aUser, boolean strict) { - String validUser = null; - boolean found = false; - if (aUser != null && !aUser.equalsIgnoreCase("undefined") && !aUser.equalsIgnoreCase("null") - && !aUser.equalsIgnoreCase("")) { - if (bridgeSettings.getWhitelist() != null) { - Set theUserIds = bridgeSettings.getWhitelist().keySet(); - Iterator userIterator = theUserIds.iterator(); - while (userIterator.hasNext()) { - validUser = userIterator.next(); - if (validUser.equals(aUser)) - found = true; - } - } - - if (!found && !strict) { - if (bridgeSettings.getWhitelist() == null) { - Map awhitelist = new HashMap<>(); - bridgeSettings.setWhitelist(awhitelist); - } - bridgeSettings.getWhitelist().put(aUser, WhitelistEntry.createEntry("auto insert user")); - bridgeSettings.setSettingsChanged(true); - found = true; - } - } - - if (!found) { - log.debug("Validate user, No User supplied"); - return HueErrorResponse.createResponse("1", "/api/" + aUser, "unauthorized user", null, null, null).getTheErrors(); - } - - return null; - } - private Boolean filterByRequester(String requesterFilterList, String anAddress) { if (requesterFilterList == null || requesterFilterList.length() == 0) return true; @@ -620,9 +574,13 @@ public class HueMulator { private String basicListHandler(String type, String userId, String requestIp) { log.debug("hue " + type + " list requested: " + userId + " from " + requestIp); - HueError[] theErrors = validateWhitelistUser(userId, false); - if (theErrors != null) + HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, false); + if (theErrors != null) { + if(bridgeSettings.isSettingsChanged()) + bridgeSettingMaster.updateConfigFile(); + return aGsonHandler.toJson(theErrors); + } return "{}"; } @@ -630,8 +588,11 @@ public class HueMulator { log.debug("hue group list requested: " + userId + " from " + requestIp); HueError[] theErrors = null; Map groupResponseMap = null; - theErrors = validateWhitelistUser(userId, false); + theErrors = bridgeSettings.validateWhitelistUser(userId, null, false); if (theErrors == null) { + if(bridgeSettings.isSettingsChanged()) + bridgeSettingMaster.updateConfigFile(); + groupResponseMap = new HashMap(); groupResponseMap.put("1", (GroupResponse) this.groupsIdHandler("1", userId, requestIp)); return groupResponseMap; @@ -644,8 +605,11 @@ public class HueMulator { private Object groupsIdHandler(String groupId, String userId, String requestIp) { log.debug("hue group id: <" + groupId + "> requested: " + userId + " from " + requestIp); HueError[] theErrors = null; - theErrors = validateWhitelistUser(userId, false); + theErrors = bridgeSettings.validateWhitelistUser(userId, null, false); if (theErrors == null) { + if(bridgeSettings.isSettingsChanged()) + bridgeSettingMaster.updateConfigFile(); + if (groupId.equalsIgnoreCase("0")) { GroupResponse theResponse = GroupResponse.createDefaultGroupResponse(repository.findActive()); return theResponse; @@ -666,8 +630,11 @@ public class HueMulator { if (bridgeSettings.isTraceupnp()) log.info("Traceupnp: hue lights list requested: " + userId + " from " + requestIp); log.debug("hue lights list requested: " + userId + " from " + requestIp); - theErrors = validateWhitelistUser(userId, false); + theErrors = bridgeSettings.validateWhitelistUser(userId, null, false); if (theErrors == null) { + if(bridgeSettings.isSettingsChanged()) + bridgeSettingMaster.updateConfigFile(); + List deviceList = repository.findAllByRequester(requestIp); // List deviceList = repository.findActive(); deviceResponseMap = new HashMap(); @@ -724,12 +691,20 @@ public class HueMulator { newUser = aNewUser.getUsername(); aDeviceType = aNewUser.getDevicetype(); } - if (newUser == null) - newUser = getNewUserID(); - validateWhitelistUser(newUser, false); if (aDeviceType == null) aDeviceType = ""; + + if (newUser == null) { + newUser = bridgeSettings.createWhitelistUser(aDeviceType); + } + else { + bridgeSettings.validateWhitelistUser(newUser, aDeviceType, false); + } + + if(bridgeSettings.isSettingsChanged()) + bridgeSettingMaster.updateConfigFile(); + if (bridgeSettings.isTraceupnp()) log.info("Traceupnp: hue api user create requested for device type: " + aDeviceType + " and username: " + newUser + (followingSlash ? " /api/ called" : "")); @@ -743,7 +718,7 @@ public class HueMulator { if (bridgeSettings.isTraceupnp()) log.info("Traceupnp: hue api/:userid/config config requested: " + userId + " from " + ipAddress); log.debug("hue api config requested: " + userId + " from " + ipAddress); - if (validateWhitelistUser(userId, true) != null) { + if (bridgeSettings.validateWhitelistUser(userId, null, true) != null) { log.debug("hue api config requested, No User supplied, returning public config"); HuePublicConfig apiResponse = HuePublicConfig.createConfig("Philips hue", bridgeSettings.getUpnpConfigAddress(), bridgeSettings.getHubversion()); @@ -759,7 +734,7 @@ public class HueMulator { @SuppressWarnings("unchecked") private Object getFullState(String userId, String ipAddress) { log.debug("hue api full state requested: " + userId + " from " + ipAddress); - HueError[] theErrors = validateWhitelistUser(userId, false); + HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true); if (theErrors != null) return theErrors; @@ -773,7 +748,7 @@ public class HueMulator { private Object getLight(String userId, String lightId, String ipAddress) { log.debug("hue light requested: " + lightId + " for user: " + userId + " from " + ipAddress); - HueError[] theErrors = validateWhitelistUser(userId, false); + HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true); if (theErrors != null) return theErrors; @@ -817,7 +792,7 @@ public class HueMulator { Integer targetBri = null; Integer targetBriInc = null; log.debug("Update state requested: " + userId + " from " + ipAddress + " body: " + body); - HueError[] theErrors = validateWhitelistUser(userId, false); + HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true); if (theErrors != null) return aGsonHandler.toJson(theErrors); try { @@ -867,7 +842,7 @@ public class HueMulator { aMultiUtil.setDelayDefault(bridgeSettings.getButtonsleep()); aMultiUtil.setSetCount(1); log.debug("hue state change requested: " + userId + " from " + ipAddress + " body: " + body); - HueError[] theErrors = validateWhitelistUser(userId, false); + HueError[] theErrors = bridgeSettings.validateWhitelistUser(userId, null, true); if (theErrors != null) return aGsonHandler.toJson(theErrors); try { diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 644290e..92a8599 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -57,6 +57,7 @@ app.config (function ($locationProvider, $routeProvider) { app.run( function (bridgeService) { bridgeService.loadBridgeSettings(); bridgeService.getHABridgeVersion(); + bridgeService.getTestUser(); bridgeService.viewMapTypes(); }); @@ -160,6 +161,17 @@ app.service ('bridgeService', function ($http, $window, ngToast) { ); }; + this.getTestUser = function () { + return $http.get(this.state.systemsbase + "/habridge/testuser").then( + function (response) { + self.state.testuser = response.data.user; + }, + function (error) { + self.displayWarn("Cannot get testuser: ", error); + } + ); + }; + this.aContainsB = function (a, b) { return a.indexOf(b) >= 0; } @@ -774,7 +786,7 @@ app.service ('bridgeService', function ($http, $window, ngToast) { this.testUrl = function (device, type, value) { var msgDescription = "unknown"; - var testUrl = this.state.huebase + "/test/lights/" + device.id + "/state"; + var testUrl = this.state.huebase + "/" + this.state.testuser + "/lights/" + device.id + "/state"; var testBody = "{\"on\":"; if (type === "off") { testBody = testBody + "false"; From c13b9bd8f496617ac02cf822382cb2dd356397dd Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 16 Mar 2017 14:57:21 -0500 Subject: [PATCH 3/5] upate for editing with fields that need not be saved when empty. --- pom.xml | 2 +- src/main/resources/public/scripts/app.js | 27 +++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3d94517..8e2c332 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 4.2.1b + 4.2.1c jar HA Bridge diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 92a8599..7a4cb83 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -147,7 +147,7 @@ app.service ('bridgeService', function ($http, $window, ngToast) { this.clearDevice = function () { self.state.device = {}; - self.state.olddevicename = ""; + self.state.olddevicename = null; }; this.getHABridgeVersion = function () { @@ -525,6 +525,20 @@ app.service ('bridgeService', function ($http, $window, ngToast) { type = self.getMapType(s.type[0]) s.type = type[0] } + if(s.delay === "" || s.delay === null) + delete s.delay; + if(s.count === "" || s.count === null) + delete s.count; + if(s.filterIPs === "" || s.filterIPs === null) + delete s.filterIPs; + if(s.httpVerb === "" || s.httpVerb === null) + delete s.httpVerb; + if(s.httpBody === "" || s.httpBody === null) + delete s.httpBody; + if(s.httpHeaders === "" || s.httpHeaders === null) + delete s.httpHeaders; + if(s.contentType === "" || s.contentType === null) + delete s.contentType; } } return theDevices @@ -2462,18 +2476,21 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi if (copy) { $scope.device.id = null; $scope.device.uniqueid = null; - $scope.device.mapId = $scope.device.mapId + "-copy"; + if($scope.bridge.olddevicename !== null && $scope.bridge.olddevicename !== "") + $scope.device.mapId = $scope.device.mapId + "-copy"; } if($scope.mapTypeSelected !== undefined && $scope.mapTypeSelected !== null) $scope.device.mapType = $scope.mapTypeSelected[0]; else $scope.device.mapType = null; + if ($scope.onDevices !== null) $scope.device.onUrl = angular.toJson(bridgeService.updateCallObjectsType($scope.onDevices)); if ($scope.dimDevices !== null) $scope.device.dimUrl = angular.toJson(bridgeService.updateCallObjectsType($scope.dimDevices)); if ($scope.offDevices !== null) $scope.device.offUrl = angular.toJson(bridgeService.updateCallObjectsType($scope.offDevices)); + bridgeService.addDevice($scope.device).then( function () { $scope.clearDevice(); @@ -2493,7 +2510,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi if ($scope.onDevices === null) $scope.onDevices = []; $scope.onDevices.push(newitem); - $scope.newOnItem = []; + $scope.newOnItem = {}; }; $scope.removeItemOn = function (anItem) { for(var i = $scope.onDevices.length - 1; i >= 0; i--) { @@ -2510,7 +2527,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi if ($scope.dimDevices === null) $scope.dimDevices = []; $scope.dimDevices.push(newitem); - $scope.newDimItem = []; + $scope.newDimItem = {}; }; $scope.removeItemDim = function (anItem) { for(var i = $scope.dimDevices.length - 1; i >= 0; i--) { @@ -2527,7 +2544,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi if ($scope.offDevices === null) $scope.offDevices = []; $scope.offDevices.push(newitem); - $scope.newOffItem = []; + $scope.newOffItem = {}; }; $scope.removeItemOff = function (anItem) { for(var i = $scope.offDevices.length - 1; i >= 0; i--) { From 0c4292bfd7515e4d810c6cbf1cd9d9ab2d68eb37 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 16 Mar 2017 17:05:40 -0500 Subject: [PATCH 4/5] Update scrollable table and try implement scroll to row. --- README.md | 4 ++-- pom.xml | 2 +- .../java/com/bwssystems/HABridge/DeviceMapTypes.java | 1 + src/main/resources/public/css/scrollable-table.css | 10 +++++----- .../public/js/angular-scrollable-table.min.js | 2 +- src/main/resources/public/scripts/app.js | 8 ++++++-- src/main/resources/public/views/configuration.html | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e04b2ff..676f54b 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ The server defaults to running on port 80. To override what the default is, spec #### UPNP Response Port The upnp response port that will be used. The default is 50000. #### Vera Names and IP Addresses -Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure. Note you have to 'turn on' a window to open it, and 'turn off' to close. +Provide IP Addresses of your Veras that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the devices or scenes by the call it receives and send it to the target Vera and device/scene you configure. #### Harmony Names and IP Addresses Provide IP Addresses of your Harmony Hubs that you want to utilize with the bridge. Also, give a meaningful name to each one so it is easy to decipher in the helper tab. When these names and IP's are given, the bridge will be able to control the activity or buttons by the call it receives and send it to the target Harmony Hub and activity/button you configure. Also, an option of webhook can be called when the activity changes on the harmony hub that will send an HTTP GET call to the the address of your choosing. This can contain the replacement variables of ${activity.id} and/or ${activity.label}. Example : http://192.168.0.1/activity/${activity.id}/${activity.label} OR http://hook?a=${activity.label} #### Hue Names and IP Addresses @@ -259,7 +259,7 @@ The password for the user name of the home.nest.com account for the Nest user. T #### Nest Temp Fahrenheit This setting allows the value being sent into the bridge to be interpreted as Fahrenheit or Celsius. The default is to have Fahrenheit. #### Somfy Tahoma Username -The user name used to login to www.tahomalink.com. This needs to be provided if you're using the Somfy Tahoma features (for connecting to IO Homecontrol used by Velux among others). There is no need to give any IP address or host information as this contacts your cloud account +The user name used to login to www.tahomalink.com. This needs to be provided if you're using the Somfy Tahoma features (for connecting to IO Homecontrol used by Velux among others). There is no need to give any IP address or host information as this contacts your cloud account. *Note:* you have to 'turn on' a window to open it, and 'turn off' to close. #### Somfy Tahoma Password The password associated with the Somfy Tahoma username above #### Button Press/Call Item Loop Sleep Interval (ms) diff --git a/pom.xml b/pom.xml index 8e2c332..ec33dd0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 4.2.1c + 4.2.1d jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java b/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java index 56a06a4..f8fae87 100644 --- a/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java +++ b/src/main/java/com/bwssystems/HABridge/DeviceMapTypes.java @@ -52,6 +52,7 @@ public class DeviceMapTypes { deviceMapTypes.add(MQTT_MESSAGE); deviceMapTypes.add(NEST_HOMEAWAY); deviceMapTypes.add(NEST_THERMO_SET); + deviceMapTypes.add(SOMFY_DEVICE); deviceMapTypes.add(TCP_DEVICE); deviceMapTypes.add(UDP_DEVICE); deviceMapTypes.add(VERA_DEVICE); diff --git a/src/main/resources/public/css/scrollable-table.css b/src/main/resources/public/css/scrollable-table.css index 39364c0..492656d 100644 --- a/src/main/resources/public/css/scrollable-table.css +++ b/src/main/resources/public/css/scrollable-table.css @@ -1,5 +1,5 @@ .scrollableContainer { - max-height: 436px; /* sets max-height value for all standards-compliant browsers */ + height: 310px; position: relative; padding-top: 35px; overflow: hidden; @@ -28,17 +28,17 @@ } .scrollArea { - _height: expression( this.scrollHeight > 599 ? "600px" : "auto" ); /* sets max-height for IE6 */ - max-height: 400px; /* sets max-height value for all standards-compliant browsers */ + height: 100%; overflow-x: auto; overflow-y: auto; border: 1px solid #d5d5d5; /* the implementation of this is still quite buggy; specifically, it doesn't like the absolutely positioned headers within -webkit-overflow-scrolling: touch; */ + -webkit-overflow-scrolling: auto; } .scrollArea table { - overflow-x: hidden; + overflow-x: auto; overflow-y: auto; margin-bottom: 0; width: 100%; @@ -48,7 +48,7 @@ .scrollArea table th { padding: 0 !important; border: none !important; - min-width: 60px; + min-width: 40px; } .scrollArea table .th-inner { overflow: hidden; diff --git a/src/main/resources/public/js/angular-scrollable-table.min.js b/src/main/resources/public/js/angular-scrollable-table.min.js index 513e1fb..93a1539 100644 --- a/src/main/resources/public/js/angular-scrollable-table.min.js +++ b/src/main/resources/public/js/angular-scrollable-table.min.js @@ -1 +1 @@ -(function(e){"use strict";function t(e){return parseInt(e.replace(/px|%/,""),10)}e.module("scrollable-table",[]).directive("scrollableTable",["$timeout","$q","$parse",function(n,r,i){return{transclude:true,restrict:"E",scope:{rows:"=watch",sortFn:"="},template:'
'+'
'+'
'+"
",controller:["$scope","$element","$attrs",function(s,o,u){function a(e,t){var n=s.sortExpr.match(/(.+)\s+as\s+(.+)/);var r={};r[n[1]]=e;var o=i(n[2])(r);r[n[1]]=t;var u=i(n[2])(r);if(o===u)return 0;return o>u?1:-1}function f(e){var t=o.find(".headerSpacer").height();var n=o.find(".scrollArea").scrollTop();o.find(".scrollArea").scrollTop(n+e.position().top-t)}function l(){function t(){if(o.find("table:visible").length===0){n(t,100)}else{e.resolve()}}var e=r.defer();n(t);return e.promise}function h(){if(!o.find("thead th .th-inner").length){o.find("thead th").wrapInner('
')}if(o.find("thead th .th-inner:not(:has(.box))").length){o.find("thead th .th-inner:not(:has(.box))").wrapInner('
')}o.find("table th .th-inner:visible").each(function(n,r){r=e.element(r);var i=r.parent().width(),s=o.find("table th:visible:last"),u=i;if(s.css("text-align")!=="center"){var a=o.find(".scrollArea").height()'+'
'+'
'+''+''+''+''+""+""+"
"+"",link:function(t,n,r,i){var s=r.on||"a as a."+r.col;t.element=e.element(n);t.isActive=function(){return i.getSortExpr()===s};t.toggleSort=function(e){if(t.isActive()){i.toggleSort()}else{i.setSortExpr(s)}i.doSort(t[r.comparatorFn]);e.preventDefault()};t.isAscending=function(){if(t.focused&&!t.isActive()){return true}else{return i.isAsc()}};t.enter=function(){t.focused=true};t.leave=function(){t.focused=false};t.isLastCol=function(){return n.parent().find("th:last-child").get(0)===n.get(0)}}}}]).directive("resizable",["$compile",function(n){return{restrict:"A",priority:0,require:"^scrollableTable",link:function(i,s,o,u){function a(){var t=s.find("table th .th-inner");if(t.find(".resize-rod").length==0){u.getTableElement().find(".scrollArea table").css("table-layout","auto");var r=e.element('
');t.append(n(r)(i))}}function f(){var n=u.getTableElement();var r=1;n.find("table th .th-inner:visible").each(function(n,i){i=e.element(i);var s=i.parent().width(),o=t(i.parent().css("min-width"));s=Math.max(o,s);i.css("left",r);r+=s})}function l(){var n=1,r=u.getTableElement();u.getTableElement().find("table th .th-inner:visible").each(function(i,s){s=e.element(s);var o=s.parent().width(),u=r.find("table th:visible:last"),a=t(s.parent().css("min-width"));o=Math.max(a,o);if(u[0]!=s.parent()[0]){s.parent().css("width",o)}s.css("left",n);n+=o})}function c(n){var r=u.getTableElement(),i=r.find("table th:visible").length,s=r.find("table th:visible:last");r.find("table th:visible").each(function(r,o){o=e.element(o);if(s.get(0)==o.get(0)){o.css("width","auto");return}var u=o.data("width");if(/\d+%$/.test(u)){u=Math.ceil(n*t(u)/100)}else{u=n/i}o.css("width",u+"px")});u.renderTalble().then(l())}u.appendTableResizingHandler(function(){a()});u.appendTableResizingHandler(function(){var t=u.getTableElement().find(".scrollArea table");if(t.css("table-layout")==="auto"){f()}else{c(t.parent().width())}});i.resizing=function(n){var r=u.getTableElement().find(".scrollArea").scrollLeft(),i=e.element(n.target).parent(),s=i.parent(),o=t(i.css("left"))+i.width()-r,a=n.pageX,f=e.element(document),c=e.element("body"),h=e.element(".scrollableContainer .resizing-cover"),p=e.element('
');c.addClass("scrollable-resizing");h.addClass("active");e.element(".scrollableContainer").append(p);p.css("left",o);f.bind("mousemove",function(e){var n=e.pageX-a,r=t(p.css("left"))-o,i=s.width(),u=t(s.css("min-width")),f=s.next().width(),l=t(s.next().css("min-width"));a=e.pageX;e.preventDefault();if(n>0&&f-r<=l||n<0&&i+r<=u){return}p.css("left",t(p.css("left"))+n)});f.bind("mouseup",function(n){n.preventDefault();p.remove();c.removeClass("scrollable-resizing");h.removeClass("active");f.unbind("mousemove");f.unbind("mouseup");var r=t(p.css("left"))-o,i=s.width(),a=t(s.css("min-width")),d=s.next().width(),v=t(s.next().css("min-width")),m=u.getTableElement().find(".scrollArea table");if(m.css("table-layout")==="auto"){m.find("th .th-inner").each(function(t,n){n=e.element(n);var r=n.parent().width();n.parent().css("width",r)})}m.css("table-layout","fixed");if(r>0&&d-r<=v){r=d-v}s.next().removeAttr("style");i+=r;s.css("width",Math.max(a,i));s.next().css("width",d-r);u.renderTalble().then(l())})}}}}])})(angular) \ No newline at end of file +!function(e){"use strict";function t(e){return parseInt(e.replace(/px|%/,""),10)}var n=navigator.userAgent.toLowerCase().indexOf("firefox")>-1;e.module("scrollable-table",[]).directive("scrollableTable",["$timeout","$q","$parse",function(i,r,s){return{transclude:!0,restrict:"E",scope:{rows:"=watch",sortFn:"="},template:'
',controller:["$scope","$element","$attrs",function(a,l,o){function c(e,t){var n=a.sortExpr.match(/(.+)\s+as\s+(.+)/),i={};i[n[1]]=e;var r=s(n[2])(i);i[n[1]]=t;var l=s(n[2])(i);return r===l?0:r>l?1:-1}function d(e){var t=l.find(".headerSpacer").height(),n=l.find(".scrollArea").scrollTop();l.find(".scrollArea").scrollTop(n+e.position().top-t)}function h(){function e(){0===l.find("table:visible").length?i(e,100):t.resolve()}var t=r.defer();return i(e),t.promise}function f(){l.find("thead th .th-inner").length||l.find("thead th").wrapInner('
'),l.find("thead th .th-inner:not(:has(.box))").length&&l.find("thead th .th-inner:not(:has(.box))").wrapInner('
'),l.find("table th .th-inner:visible").each(function(n,i){i=e.element(i);var r=i.parent().width(),s=l.find("table th:visible:last"),a=r;if("center"!==s.css("text-align")){var o=l.find(".scrollArea").height()
',link:function(t,n,i,r){var s=i.on||"a as a."+i.col;t.element=e.element(n),t.isActive=function(){return r.getSortExpr()===s},t.toggleSort=function(e){t.isActive()?r.toggleSort():r.setSortExpr(s),r.doSort(t[i.comparatorFn]),e.preventDefault()},t.isAscending=function(){return!(!t.focused||t.isActive())||r.isAsc()},t.enter=function(){t.focused=!0},t.leave=function(){t.focused=!1},t.isLastCol=function(){return n.parent().find("th:last-child").get(0)===n.get(0)}}}}]).directive("resizable",["$compile",function(n){return{restrict:"A",priority:0,scope:!1,require:"scrollableTable",link:function(i,r,s,a){function l(){var t=r.find("table th:not(:last-child) .th-inner");if(0==t.find(".resize-rod").length){a.getTableElement().find(".scrollArea table").css("table-layout","auto");var s=e.element('
');t.append(n(s)(i))}}function o(){var n=a.getTableElement(),i=1;n.find("table th .th-inner:visible").each(function(n,r){r=e.element(r);var s=r.parent().width(),a=t(r.parent().css("min-width"));s=Math.max(a,s),r.css("left",i),i+=s})}function c(){var n=1,i=a.getTableElement();a.getTableElement().find("table th .th-inner:visible").each(function(r,s){s=e.element(s);var a=s.parent().width(),l=i.find("table th:visible:last"),o=t(s.parent().css("min-width"));a=Math.max(o,a),l[0]!=s.parent()[0]&&s.parent().css("width",a),s.css("left",n),n+=a})}function d(n){var i=a.getTableElement(),r=i.find("table th:visible").length,s=i.find("table th:visible:last");i.find("table th:visible").each(function(i,a){if(a=e.element(a),s.get(0)==a.get(0))return void a.css("width","auto");var l=a.data("width");l=/\d+%$/.test(l)?Math.ceil(n*t(l)/100):n/r,a.css("width",l+"px")}),a.renderTalble().then(c())}a.appendTableResizingHandler(function(){l()}),a.appendTableResizingHandler(function(){var e=a.getTableElement().find(".scrollArea table");"auto"===e.css("table-layout")?o():d(e.parent().width())}),i.resizing=function(n){var i=a.getTableElement().find(".scrollArea").scrollLeft(),r=e.element(n.target).parent(),s=r.parent(),l=t(r.css("left"))+r.width()-i,o=n.pageX,d=e.element(document),h=e.element("body"),f=e.element(".scrollableContainer .resizing-cover"),u=e.element('
');h.addClass("scrollable-resizing"),f.addClass("active"),e.element(".scrollableContainer").append(u),u.css("left",l),d.bind("mousemove",function(e){var n=e.pageX-o,i=t(u.css("left"))-l,r=s.width(),a=s.nextAll("th:visible").first(),c=t(s.css("min-width")),d=a.width(),h=t(a.css("min-width"));o=e.pageX,e.preventDefault(),n>0&&d-i<=h||n<0&&r+i<=c||u.css("left",t(u.css("left"))+n)}),d.bind("mouseup",function(n){n.preventDefault(),u.remove(),h.removeClass("scrollable-resizing"),f.removeClass("active"),d.unbind("mousemove"),d.unbind("mouseup");var i=t(u.css("left"))-l,r=s.width(),o=t(s.css("min-width")),v=s.nextAll("th:visible").first(),p=v.width(),b=t(v.css("min-width")),g=a.getTableElement().find(".scrollArea table");"auto"===g.css("table-layout")&&g.find("th .th-inner").each(function(t,n){n=e.element(n);var i=n.parent().width();n.parent().css("width",i)}),g.css("table-layout","fixed"),i>0&&p-i<=b&&(i=p-b),v.removeAttr("style"),r+=i,s.css("width",Math.max(o,r)),v.css("width",p-i),a.renderTalble().then(c())})}}}}])}(angular); \ No newline at end of file diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 7b73e04..7817014 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -80,7 +80,7 @@ String.prototype.replaceAll = function (search, replace) app.service ('bridgeService', function ($http, $window, ngToast) { var self = this; - this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: ""}; + this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: ""}; this.displayWarn = function(errorTitle, error) { var toastContent = errorTitle; @@ -1231,7 +1231,11 @@ app.controller('ViewingController', function ($scope, $location, $http, $window, else $scope.imgBkUrl = "glyphicon glyphicon-plus"; }; -}); + + $scope.$watch('bridge.devices', function(devId) { + $scope.$broadcast("rowSelected", bridgeService.state.viewDevId); + }); + }); app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) { $scope.slider = { diff --git a/src/main/resources/public/views/configuration.html b/src/main/resources/public/views/configuration.html index 14dc9b4..9ebf4df 100644 --- a/src/main/resources/public/views/configuration.html +++ b/src/main/resources/public/views/configuration.html @@ -49,7 +49,7 @@ Actions - + {{$index+1}} {{device.id}} {{device.name}} From bb0ffeb570ec72963d27920e923e5d57d50e6c8d Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 17 Mar 2017 16:53:21 -0500 Subject: [PATCH 5/5] Added device data value passing, off state change for brigthness, hex intensity values, updatd ui to cgo back to device touched after edit/add. --- README.md | 10 +- pom.xml | 2 +- .../HABridge/dao/DeviceDescriptor.java | 33 +++++ .../HABridge/hue/BrightnessDecode.java | 125 ++++++++++++------ .../bwssystems/HABridge/hue/ColorDecode.java | 21 +++ .../HABridge/hue/DeviceDataDecode.java | 66 +++++++++ .../bwssystems/HABridge/hue/HueMulator.java | 10 +- .../bwssystems/HABridge/hue/TimeDecode.java | 26 ++-- .../HABridge/plugins/exec/CommandHome.java | 2 + .../HABridge/plugins/harmony/HarmonyHome.java | 3 + .../HABridge/plugins/http/HTTPHome.java | 5 +- .../HABridge/plugins/mqtt/MQTTHome.java | 2 + .../HABridge/plugins/tcp/TCPHome.java | 3 + .../HABridge/plugins/udp/UDPHome.java | 3 + src/main/resources/public/scripts/app.js | 35 ++++- .../resources/public/views/configuration.html | 6 +- .../resources/public/views/editdevice.html | 18 +++ .../color/test/ConvertCIEColorTestCase.java | 21 +++ 18 files changed, 326 insertions(+), 65 deletions(-) create mode 100644 src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java create mode 100644 src/main/java/com/bwssystems/HABridge/hue/DeviceDataDecode.java create mode 100644 src/test/java/com/bwssystems/color/test/ConvertCIEColorTestCase.java diff --git a/README.md b/README.md index 676f54b..f30b89a 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ There is a new format for the on/dim/off URL areas. The new editor handles the i Here are the fields that can be put into the call item: Json Type | field name | What | Use ----------|------------|------|----- -String or JsonElement | item| This is the payload that will be called for devices | Required +String or JsonElement | item | This is the payload that will be called for devices | Required Integer | count | This is how many times this items will be executed | Optional Integer | delay | This is how long we will wait until the next call after | Optional String | type | This is the type of device we are executing | Required @@ -332,7 +332,7 @@ Headers can be added as well using a Json construct [{"name":"header type name", Another option that is detected by the bridge is to use UDP or TCP direct calls such as `udp://:/` to send a UDP request. TCP calls are handled the same way as `tcp://:/`. If your data for the UDP or TCP request is formatted as "0x00F009B9" lexical hex format, the bridge will convert the data into a binary stream to send. -You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". See Value Passing Controls Below. +You can also use the value replacement constructs within these statements. Such as using the expressions "${time.format(Java time format string)}" for inserting a date/time stamp, ${intensity.percent} or ${intensity.percent.hex} for 0-100 or ${intensity.decimal_percent} for 0.00-1.00 or ${intensity.byte} or ${intensity.byte.hex} for 0-255 for straight pass through of the value or items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}" or "${intensity.math(X/4).hex}". See Value Passing Controls Below. Examples: ``` @@ -382,9 +382,13 @@ In the URL areas, the format of the execution is just providing what command lin [{"item":"/home/pi/scripts/dothisscript.sh","type":"cmdDevice"}] ``` #### Value Passing Controls -There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.decimal_percent}", "${intensity.byte}" and "${intensity.math(using X in your calc)}". +There are multiple replacement constructs available to be put into any of the calls except Harmony items, Net Items and HAL items. These constructs are: "${time.format(Java time format string)}", "${intensity.percent}", "${intensity.percent.hex}", "${intensity.decimal_percent}", "${intensity.byte}", "${intensity.byte.hex}", "${intensity.math(using X in your calc)}" and "${intensity.math(using X in your calc).hex}". + You can control items that require special calculated values using ${intensity.math()} i.e. "${intensity.math(X/4)}". + For the items that want to have a date time put into the message, utilize ${time.format(yyyy-MM-ddTHH:mm:ssXXX)} where "yyyy-MM-ddTHH:mm:ssXXX" can be any format from the Java SimpleDateFormat documented here: https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html + +Also, device data can be inserted into your payloads by the use of "${device.name}", "${device.id}", "${device.uniqueid}", "${device.targetDevice}", "${device.mapId}", "${device.mapType}" and "${device.deviceType}". These work just like the dimming value replacements. e.g. ``` [{"item":"http://192.168.1.201:3480/data_request?id=action&output_format=json&DeviceNum=10&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=${intensity.math(X/4)}","type":"httpDevice"}] diff --git a/pom.xml b/pom.xml index ec33dd0..c147bbf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 4.2.1d + 4.3.0 jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java index 547b04e..c31b0d0 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java @@ -62,9 +62,18 @@ public class DeviceDescriptor{ @SerializedName("noState") @Expose private boolean noState; + @SerializedName("offState") + @Expose + private boolean offState; @SerializedName("requesterAddress") @Expose private String requesterAddress; + @SerializedName("description") + @Expose + private String description; + @SerializedName("comments") + @Expose + private String comments; private DeviceState deviceState; @@ -222,6 +231,14 @@ public class DeviceDescriptor{ this.noState = noState; } + public boolean isOffState() { + return offState; + } + + public void setOffState(boolean offState) { + this.offState = offState; + } + public String getRequesterAddress() { return requesterAddress; } @@ -230,6 +247,22 @@ public class DeviceDescriptor{ this.requesterAddress = requesterAddress; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + public boolean containsType(String aType) { if(aType == null) return false; diff --git a/src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java b/src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java index ff6c186..f715ef8 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java +++ b/src/main/java/com/bwssystems/HABridge/hue/BrightnessDecode.java @@ -17,6 +17,9 @@ public class BrightnessDecode { private static final String INTENSITY_MATH = "${intensity.math("; private static final String INTENSITY_MATH_VALUE = "X"; private static final String INTENSITY_MATH_CLOSE = ")}"; + private static final String INTENSITY_MATH_CLOSE_HEX = ").hex}"; + private static final String INTENSITY_PERCENT_HEX = "${intensity.percent.hex}"; + private static final String INTENSITY_BYTE_HEX = "${intensity.byte.hex}"; public static int calculateIntensity(int setIntensity, Integer targetBri, Integer targetBriInc) { if (targetBri != null) { @@ -45,50 +48,79 @@ public class BrightnessDecode { if (request == null) { return null; } - if (request.contains(INTENSITY_BYTE)) { - if (isHex) { - String hexValue = convertToHex(intensity); - request = request.replace(INTENSITY_BYTE, hexValue); - } else { - String intensityByte = String.valueOf(intensity); - request = request.replace(INTENSITY_BYTE, intensityByte); - } - } else if (request.contains(INTENSITY_PERCENT)) { - int percentBrightness = (int) Math.round(intensity / 255.0 * 100); - if (isHex) { - String hexValue = convertToHex(percentBrightness); - request = request.replace(INTENSITY_PERCENT, hexValue); - } else { - String intensityPercent = String.valueOf(percentBrightness); - request = request.replace(INTENSITY_PERCENT, intensityPercent); - } - } else if (request.contains(INTENSITY_DECIMAL_PERCENT)) { - float decimalBrightness = (float) (intensity / 255.0); - - String intensityPercent = String.format("%1.2f", decimalBrightness); - request = request.replace(INTENSITY_DECIMAL_PERCENT, intensityPercent); - } else if (request.contains(INTENSITY_MATH)) { - Map variables = new HashMap(); - String mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(), - request.indexOf(INTENSITY_MATH_CLOSE)); - variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity)); - - try { + boolean notDone = true; + String replaceValue = null; + String replaceTarget = null; + int percentBrightness = (int) Math.round(intensity / 255.0 * 100); + float decimalBrightness = (float) (intensity / 255.0); + Map variables = new HashMap(); + String mathDescriptor = null; + + while(notDone) { + notDone = false; + if (request.contains(INTENSITY_BYTE)) { + if (isHex) { + replaceValue = convertToHex(intensity); + } else { + replaceValue = String.valueOf(intensity); + } + replaceTarget = INTENSITY_BYTE; + notDone = true; + } else if (request.contains(INTENSITY_BYTE_HEX)) { + replaceValue = convertToHex(intensity); + replaceTarget = INTENSITY_BYTE_HEX; + notDone = true; + } else if (request.contains(INTENSITY_PERCENT)) { + if (isHex) { + replaceValue = convertToHex(percentBrightness); + } else { + replaceValue = String.valueOf(percentBrightness); + } + replaceTarget = INTENSITY_PERCENT; + notDone = true; + } else if (request.contains(INTENSITY_PERCENT_HEX)) { + replaceValue = convertToHex(percentBrightness); + replaceTarget = INTENSITY_PERCENT_HEX; + notDone = true; + } else if (request.contains(INTENSITY_DECIMAL_PERCENT)) { + replaceValue = String.format("%1.2f", decimalBrightness); + replaceTarget = INTENSITY_DECIMAL_PERCENT; + notDone = true; + } else if (request.contains(INTENSITY_MATH_CLOSE)) { + mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(), + request.indexOf(INTENSITY_MATH_CLOSE)); + variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity)); + log.debug("Math eval is: " + mathDescriptor + ", Where " + INTENSITY_MATH_VALUE + " is: " + String.valueOf(intensity)); - Expression exp = new Expression(mathDescriptor); - BigDecimal result = exp.eval(variables); - Integer endResult = Math.round(result.floatValue()); - if (isHex) { - String hexValue = convertToHex(endResult); - request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, hexValue); - } else { - request = request.replace(INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE, - endResult.toString()); + Integer endResult = calculateMath(variables, mathDescriptor); + if(endResult != null) { + if (isHex) { + replaceValue = convertToHex(endResult); + } else { + replaceValue = endResult.toString(); + } + replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE; + notDone = true; + } + } else if (request.contains(INTENSITY_MATH_CLOSE_HEX)) { + mathDescriptor = request.substring(request.indexOf(INTENSITY_MATH) + INTENSITY_MATH.length(), + request.indexOf(INTENSITY_MATH_CLOSE_HEX)); + variables.put(INTENSITY_MATH_VALUE, new BigDecimal(intensity)); + + Integer endResult = calculateMath(variables, mathDescriptor); + if(endResult != null) { + if (isHex) { + replaceValue = convertToHex(endResult); + } else { + replaceValue = endResult.toString(); + } + replaceTarget = INTENSITY_MATH + mathDescriptor + INTENSITY_MATH_CLOSE_HEX; + notDone = true; } - } catch (Exception e) { - log.warn("Could not execute Math: " + mathDescriptor, e); } + if(notDone) + request = request.replace(replaceTarget, replaceValue); } return request; } @@ -108,4 +140,17 @@ public class BrightnessDecode { newBytes[1] = theBytes[0]; return new String(newBytes); } + + private static Integer calculateMath(Map variables, String mathDescriptor) { + Integer endResult = null; + try { + Expression exp = new Expression(mathDescriptor); + BigDecimal result = exp.eval(variables); + endResult = Math.round(result.floatValue()); + } catch (Exception e) { + log.warn("Could not execute Math: " + mathDescriptor, e); + endResult = null; + } + return endResult; + } } \ No newline at end of file diff --git a/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java b/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java new file mode 100644 index 0000000..1585d45 --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/hue/ColorDecode.java @@ -0,0 +1,21 @@ +package com.bwssystems.HABridge.hue; + +import java.util.List; + +public class ColorDecode { + + public static String convertCIEtoRGB(List xy) { + double x; + double y; + double Y; + + x = xy.get(0) * 100; + y = xy.get(1) * 100; + Y= y; + double R = 3.240479*((x*Y)/y) + -1.537150*Y + -0.498535*(((1-x-y)*Y)/y); + double G = -0.969256*((x*Y)/y) + 1.875992*Y + 0.041556*(((1-x-y)*Y)/y); + double B = 0.055648*((x*Y)/y) + -0.204043*Y + 1.057311*(((1-x-y)*Y)/y); + + return null; + } +} diff --git a/src/main/java/com/bwssystems/HABridge/hue/DeviceDataDecode.java b/src/main/java/com/bwssystems/HABridge/hue/DeviceDataDecode.java new file mode 100644 index 0000000..7997d15 --- /dev/null +++ b/src/main/java/com/bwssystems/HABridge/hue/DeviceDataDecode.java @@ -0,0 +1,66 @@ +package com.bwssystems.HABridge.hue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bwssystems.HABridge.dao.DeviceDescriptor; + +public class DeviceDataDecode { + private static final Logger log = LoggerFactory.getLogger(DeviceDataDecode.class); + private static final String DEVICE_ID = "${device.id}"; + private static final String DEVICE_UNIQUEID = "${device.uniqueid}"; + private static final String DEVICE_NAME = "${device.name}"; + private static final String DEVICE_MAPID = "${device.mapId}"; + private static final String DEVICE_MAPTYPE = "${device.mapType}"; + private static final String DEVICE_DEVICETYPE = "${device.deviceType}"; + private static final String DEVICE_TARGETDEVICE = "${device.targetDevice}"; + + public static String replaceDeviceData(String request, DeviceDescriptor device) { + if (request == null) { + return null; + } + boolean notDone = true; + + while(notDone) { + notDone = false; + if (request.contains(DEVICE_ID)) { + request = request.replace(DEVICE_ID, device.getId()); + notDone = true; + } + + if (request.contains(DEVICE_UNIQUEID)) { + request = request.replace(DEVICE_UNIQUEID, device.getUniqueid()); + notDone = true; + } + + if (request.contains(DEVICE_NAME)) { + request = request.replace(DEVICE_NAME, device.getName()); + notDone = true; + } + + if (request.contains(DEVICE_MAPID)) { + request = request.replace(DEVICE_MAPID, device.getMapId()); + notDone = true; + } + + if (request.contains(DEVICE_MAPTYPE)) { + request = request.replace(DEVICE_MAPTYPE, device.getMapType()); + notDone = true; + } + + if (request.contains(DEVICE_DEVICETYPE)) { + request = request.replace(DEVICE_DEVICETYPE, device.getDeviceType()); + notDone = true; + } + + if (request.contains(DEVICE_TARGETDEVICE)) { + request = request.replace(DEVICE_TARGETDEVICE, device.getTargetDevice()); + notDone = true; + } + + log.debug("Request <<" + request + ">>, not done: " + notDone); + } + return request; + } + +} diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index fa1b439..bab604f 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -391,7 +391,7 @@ public class HueMulator { } private String formatSuccessHueResponse(StateChangeBody stateChanges, String body, String lightId, - DeviceState deviceState, Integer targetBri, Integer targetBriInc) { + DeviceState deviceState, Integer targetBri, Integer targetBriInc, boolean offState) { String responseString = "["; boolean notFirstChange = false; @@ -406,6 +406,8 @@ public class HueMulator { deviceState.setOn(stateChanges.isOn()); if(!deviceState.isOn() && deviceState.getBri() == 254) deviceState.setBri(0); + if(!deviceState.isOn() && offState) + deviceState.setBri(0); } notFirstChange = true; } @@ -824,7 +826,7 @@ public class HueMulator { if (state == null) state = DeviceState.createDeviceState(); - responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc); + responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc, device.isOffState()); device.setDeviceState(state); return responseString; @@ -964,11 +966,11 @@ public class HueMulator { if (responseString == null || !responseString.contains("[{\"error\":")) { if(!device.isNoState()) { - responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc); + responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, state, targetBri, targetBriInc, device.isOffState()); device.setDeviceState(state); } else { DeviceState dummyState = DeviceState.createDeviceState(); - responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, dummyState, targetBri, targetBriInc); + responseString = this.formatSuccessHueResponse(theStateChanges, body, lightId, dummyState, targetBri, targetBriInc, device.isOffState()); } } return responseString; diff --git a/src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java b/src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java index 6d1c835..62343f7 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java +++ b/src/main/java/com/bwssystems/HABridge/hue/TimeDecode.java @@ -21,16 +21,22 @@ public class TimeDecode { if (request == null) { return null; } - if (request.contains(TIME_FORMAT)) { - String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(), - request.indexOf(TIME_FORMAT_CLOSE)); - - try { - log.debug("Time eval is: " + timeFormatDescriptor); - SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor); - request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date())); - } catch (Exception e) { - log.warn("Could not format current time: " + timeFormatDescriptor, e); + boolean notDone = true; + + while(notDone) { + notDone = false; + if (request.contains(TIME_FORMAT)) { + String timeFormatDescriptor = request.substring(request.indexOf(TIME_FORMAT) + TIME_FORMAT.length(), + request.indexOf(TIME_FORMAT_CLOSE)); + + try { + log.debug("Time eval is: " + timeFormatDescriptor); + SimpleDateFormat dateFormat = new SimpleDateFormat(timeFormatDescriptor); + request = request.replace(TIME_FORMAT + timeFormatDescriptor + TIME_FORMAT_CLOSE, dateFormat.format(new Date())); + notDone = true; + } catch (Exception e) { + log.warn("Could not format current time: " + timeFormatDescriptor, e); + } } } return request; diff --git a/src/main/java/com/bwssystems/HABridge/plugins/exec/CommandHome.java b/src/main/java/com/bwssystems/HABridge/plugins/exec/CommandHome.java index 0879188..2ddaf35 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/exec/CommandHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/exec/CommandHome.java @@ -10,6 +10,7 @@ import com.bwssystems.HABridge.Home; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.hue.BrightnessDecode; +import com.bwssystems.HABridge.hue.DeviceDataDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.bwssystems.HABridge.hue.TimeDecode; @@ -31,6 +32,7 @@ public class CommandHome implements Home { else intermediate = anItem.getItem().getAsString(); intermediate = BrightnessDecode.calculateReplaceIntensityValue(intermediate, itensity, targetBri, targetBriInc, false); + intermediate = DeviceDataDecode.replaceDeviceData(intermediate, device); intermediate = TimeDecode.replaceTimeValue(intermediate); String anError = doExecRequest(intermediate, lightId); if (anError != null) { diff --git a/src/main/java/com/bwssystems/HABridge/plugins/harmony/HarmonyHome.java b/src/main/java/com/bwssystems/HABridge/plugins/harmony/HarmonyHome.java index 5857f08..11d2c97 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/harmony/HarmonyHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/harmony/HarmonyHome.java @@ -17,6 +17,7 @@ import com.bwssystems.HABridge.IpList; import com.bwssystems.HABridge.NamedIP; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.dao.DeviceDescriptor; +import com.bwssystems.HABridge.hue.BrightnessDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -161,6 +162,8 @@ public class HarmonyHome implements Home { if (url.substring(0, 1).equalsIgnoreCase("{")) { url = "[" + url + "]"; } + + url = BrightnessDecode.calculateReplaceIntensityValue(url, intensity, targetBri, targetBriInc, false); ButtonPress[] deviceButtons = aGsonHandler.fromJson(url, ButtonPress[].class); Integer theCount = 1; for(int z = 0; z < deviceButtons.length; z++) { diff --git a/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java b/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java index 985fd7e..6e52459 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/http/HTTPHome.java @@ -11,6 +11,7 @@ import com.bwssystems.HABridge.api.hue.HueError; import com.bwssystems.HABridge.api.hue.HueErrorResponse; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.hue.BrightnessDecode; +import com.bwssystems.HABridge.hue.DeviceDataDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.bwssystems.HABridge.hue.TimeDecode; import com.google.gson.Gson; @@ -49,12 +50,14 @@ public class HTTPHome implements Home { String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrl, intensity, targetBri, targetBriInc, false); - + anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device); anUrl = TimeDecode.replaceTimeValue(anUrl); + String aBody = null; if(anItem.getHttpBody()!= null && !anItem.getHttpBody().isEmpty()) { aBody = BrightnessDecode.calculateReplaceIntensityValue(anItem.getHttpBody(), intensity, targetBri, targetBriInc, false); + aBody = DeviceDataDecode.replaceDeviceData(aBody, device); aBody = TimeDecode.replaceTimeValue(aBody); } // make call diff --git a/src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java b/src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java index 79212c1..0b490aa 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/mqtt/MQTTHome.java @@ -14,6 +14,7 @@ import com.bwssystems.HABridge.NamedIP; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.hue.BrightnessDecode; +import com.bwssystems.HABridge.hue.DeviceDataDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.bwssystems.HABridge.hue.TimeDecode; import com.google.gson.Gson; @@ -89,6 +90,7 @@ public class MQTTHome implements Home { mqttObject =anItem.getItem().getAsString(); mqttObject = BrightnessDecode.calculateReplaceIntensityValue(mqttObject, intensity, targetBri, targetBriInc, false); + mqttObject = DeviceDataDecode.replaceDeviceData(mqttObject, device); mqttObject = TimeDecode.replaceTimeValue(mqttObject); if (mqttObject.substring(0, 1).equalsIgnoreCase("{")) mqttObject = "[" + mqttObject + "]"; diff --git a/src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java b/src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java index 53cb664..d359e96 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/tcp/TCPHome.java @@ -20,6 +20,7 @@ import com.bwssystems.HABridge.Home; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.hue.BrightnessDecode; +import com.bwssystems.HABridge.hue.DeviceDataDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.bwssystems.HABridge.hue.TimeDecode; @@ -71,9 +72,11 @@ public class TCPHome implements Home { theUrlBody = TimeDecode.replaceTimeValue(theUrlBody); if (theUrlBody.startsWith("0x")) { theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true); + theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device); sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2)); } else { theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false); + theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device); theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody); sendData = theUrlBody.getBytes(); } diff --git a/src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java b/src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java index 1244bff..3d49881 100644 --- a/src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java +++ b/src/main/java/com/bwssystems/HABridge/plugins/udp/UDPHome.java @@ -15,6 +15,7 @@ import com.bwssystems.HABridge.Home; import com.bwssystems.HABridge.api.CallItem; import com.bwssystems.HABridge.dao.DeviceDescriptor; import com.bwssystems.HABridge.hue.BrightnessDecode; +import com.bwssystems.HABridge.hue.DeviceDataDecode; import com.bwssystems.HABridge.hue.MultiCommandUtil; import com.bwssystems.HABridge.hue.TimeDecode; import com.bwssystems.HABridge.util.UDPDatagramSender; @@ -57,9 +58,11 @@ public class UDPHome implements Home { theUrlBody = TimeDecode.replaceTimeValue(theUrlBody); if (theUrlBody.startsWith("0x")) { theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, true); + theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device); sendData = DatatypeConverter.parseHexBinary(theUrlBody.substring(2)); } else { theUrlBody = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody, intensity, targetBri, targetBriInc, false); + theUrlBody = DeviceDataDecode.replaceDeviceData(theUrlBody, device); theUrlBody = StringEscapeUtils.unescapeJava(theUrlBody); sendData = theUrlBody.getBytes(); } diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 7817014..f01e6ee 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -80,7 +80,7 @@ String.prototype.replaceAll = function (search, replace) app.service ('bridgeService', function ($http, $window, ngToast) { var self = this; - this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: ""}; + this.state = {base: "./api/devices", bridgelocation: ".", systemsbase: "./system", huebase: "./api", configs: [], backups: [], devices: [], device: {}, mapandid: [], type: "", settings: [], myToastMsg: [], logMsgs: [], loggerInfo: [], mapTypes: [], olddevicename: "", logShowAll: false, isInControl: false, showVera: false, showHarmony: false, showNest: false, showHue: false, showHal: false, showMqtt: false, showHass: false, showDomoticz: false, showSomfy: false, showLifx: false, habridgeversion: "", viewDevId: "", queueDevId: ""}; this.displayWarn = function(errorTitle, error) { var toastContent = errorTitle; @@ -1166,6 +1166,26 @@ app.controller('LogsController', function ($scope, $location, $http, $window, br }; }); +app.directive('postrenderAction', postrenderAction); +/* @ngInject */ +function postrenderAction($timeout) { + // ### Directive Interface + // Defines base properties for the directive. + var directive = { + restrict: 'A', + priority: 101, + link: link + }; + return directive; + + // ### Link Function + // Provides functionality for the directive during the DOM building/data binding stage. + function link(scope, element, attrs) { + $timeout(function() { + scope.$evalAsync(attrs.postrenderAction); + }, 0); + } +} app.controller('ViewingController', function ($scope, $location, $http, $window, bridgeService, ngDialog) { bridgeService.viewDevices(); @@ -1232,9 +1252,14 @@ app.controller('ViewingController', function ($scope, $location, $http, $window, $scope.imgBkUrl = "glyphicon glyphicon-plus"; }; - $scope.$watch('bridge.devices', function(devId) { - $scope.$broadcast("rowSelected", bridgeService.state.viewDevId); - }); + $scope.goToRow = function() { + if (bridgeService.state.queueDevId !== null && bridgeService.state.queueDevId !== "") { + bridgeService.state.viewDevId = bridgeService.state.queueDevId; + $scope.$broadcast("rowSelected", bridgeService.state.viewDevId); + console.log("Go to Row selected Id <<" + bridgeService.state.viewDevId + ">>") + bridgeService.state.queueDevId = null; + } + }; }); app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) { @@ -2669,6 +2694,8 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi bridgeService.addDevice($scope.device).then( function () { + bridgeService.state.queueDevId = $scope.device.id; + console.log("Device updated for Q Id <<" + bridgeService.state.queueDevId + ">>") $scope.clearDevice(); $location.path('/'); }, diff --git a/src/main/resources/public/views/configuration.html b/src/main/resources/public/views/configuration.html index 9ebf4df..3cb3123 100644 --- a/src/main/resources/public/views/configuration.html +++ b/src/main/resources/public/views/configuration.html @@ -24,7 +24,7 @@
  • LIFX Devices
  • Add/Edit
  • - +

    Current devices ({{bridge.devices.length}})

    @@ -34,7 +34,6 @@

    - @@ -42,6 +41,7 @@ + @@ -53,6 +53,7 @@ + @@ -76,6 +77,7 @@ +

    diff --git a/src/main/resources/public/views/editdevice.html b/src/main/resources/public/views/editdevice.html index 759b10b..ae1439d 100644 --- a/src/main/resources/public/views/editdevice.html +++ b/src/main/resources/public/views/editdevice.html @@ -65,6 +65,18 @@

    + + + + + + + + + + + + + +
    Row ID NameDescription Type Target Inactive{{$index+1}} {{device.id}} {{device.name}}{{device.description}} {{device.deviceType}} {{device.targetDevice}} {{device.inactive}}
    {{device.noState}}
    {{device.offState}}
    xy = new ArrayList(Arrays.asList(new Double(0.3972), new Double(0.4564))); + + String colorDecode = ColorDecode.convertCIEtoRGB(xy); + Assert.assertEquals(colorDecode, null); + } + +}