From 2dbf4c96c4763f94bf2df89b8faa631aa5ba609e Mon Sep 17 00:00:00 2001 From: BWS Systems Date: Wed, 12 Jun 2019 16:05:29 -0500 Subject: [PATCH] Finished config up/down load impl and Finished startup action implementation --- pom.xml | 2 +- .../bwssystems/HABridge/SystemControl.java | 35 ++++ .../HABridge/dao/DeviceDescriptor.java | 11 ++ .../HABridge/dao/DeviceRepository.java | 24 ++- .../bwssystems/HABridge/hue/HueMulator.java | 48 ++++++ src/main/resources/public/scripts/app.js | 153 +++++++++++++++++- .../resources/public/views/configuration.html | 69 ++++++-- src/main/resources/public/views/system.html | 18 ++- 8 files changed, 335 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 18a9cc7..dc31782 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 5.2.next_e + 5.3.0RC1 jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/SystemControl.java b/src/main/java/com/bwssystems/HABridge/SystemControl.java index ce2eb8f..e83d770 100644 --- a/src/main/java/com/bwssystems/HABridge/SystemControl.java +++ b/src/main/java/com/bwssystems/HABridge/SystemControl.java @@ -437,6 +437,41 @@ public class SystemControl { return stop(); }); + // http://ip_address:port/system/devices/backup/download CORS request + options(SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> { + response.status(HttpStatus.SC_OK); + response.header("Access-Control-Allow-Origin", request.headers("Origin")); + response.header("Access-Control-Allow-Methods", "PUT"); + response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); + response.header("Content-Type", "text/html; charset=utf-8"); + return ""; + }); + put (SYSTEM_CONTEXT + "/backup/download", "application/json", (request, response) -> { + log.debug("Create download: {}", request.body()); + BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class); + String backupContent = bridgeSettings.downloadBackup(aFilename.getFilename()); + return backupContent; + }, new JsonTransformer()); + + // http://ip_address:port/system/devices/backup/upload CORS request + options(SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> { + response.status(HttpStatus.SC_OK); + response.header("Access-Control-Allow-Origin", request.headers("Origin")); + response.header("Access-Control-Allow-Methods", "PUT"); + response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); + response.header("Content-Type", "text/html; charset=utf-8"); + return ""; + }); + put (SYSTEM_CONTEXT + "/backup/upload/:filename", "application/json", (request, response) -> { + log.debug("Create upload: {} - {}", request.params(":filename"), request.body()); + String theSuccess = bridgeSettings.uploadBackup(request.params(":filename"), request.body()); + if(theSuccess.contains("Error:")) + response.status(HttpStatus.SC_METHOD_FAILURE); + else + response.status(HttpStatus.SC_OK); + return theSuccess; + }, new JsonTransformer()); + // http://ip_address:port/system/backup/available returns a list of config backup filenames get (SYSTEM_CONTEXT + "/backup/available", (request, response) -> { log.debug("Get backup filenames"); diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java index 63dff65..d24d563 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceDescriptor.java @@ -89,6 +89,9 @@ public class DeviceDescriptor{ @SerializedName("lockDeviceId") @Expose private boolean lockDeviceId; + @SerializedName("startupActions") + @Expose + private String startupActions; public String getName() { return name; @@ -344,4 +347,12 @@ public class DeviceDescriptor{ public void setLockDeviceId(boolean lockDeviceId) { this.lockDeviceId = lockDeviceId; } + + public String getStartupActions() { + return startupActions; + } + + public void setStartupActions(String startupActions) { + this.startupActions = startupActions; + } } \ No newline at end of file diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java index 4442ade..59cd5c2 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java @@ -202,15 +202,33 @@ public class DeviceRepository extends BackupHandler { List list = new ArrayList(devices.values()); Iterator deviceIterator = list.iterator(); Map newdevices = new HashMap(); - - nextId = seedId; + List lockedIds = new ArrayList(); String hexValue; Integer newValue; DeviceDescriptor theDevice; - log.debug("Renumber devices with seed: {}", seedId); + boolean findNext = true; + + + nextId = seedId; + while(deviceIterator.hasNext()) { + theDevice = deviceIterator.next(); + if(theDevice.isLockDeviceId()) { + lockedIds.add(theDevice.getId()); + } + } + log.debug("Renumber devices starting with: {}", nextId); + deviceIterator = list.iterator(); while (deviceIterator.hasNext()) { theDevice = deviceIterator.next(); if (!theDevice.isLockDeviceId()) { + findNext = true; + while(findNext) { + if(lockedIds.contains(String.valueOf(nextId))) { + nextId++; + } else { + findNext = false; + } + } theDevice.setId(String.valueOf(nextId)); newValue = nextId % 256; if (newValue <= 0) diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index 00953ad..7548471 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.awt.Color; import java.util.Arrays; /** @@ -77,6 +78,7 @@ public class HueMulator { // This function sets up the sparkjava rest calls for the hue api public void setupServer() { log.info("Hue emulator service started...."); + startupDeviceCall(); before(HUE_CONTEXT + "/*", (request, response) -> { // This currently causes an error with Spark replies // String path = request.pathInfo(); @@ -1671,4 +1673,50 @@ public class HueMulator { return responseString; } + + private void startupDeviceCall() { + String aUserId = bridgeSettingMaster.getBridgeSecurity().createWhitelistUser("test_ha_bridge"); + List deviceList = repository.findAll(); + String aChangeBody; + String[] components; + boolean comma = false; + + for (DeviceDescriptor aDevice : deviceList) { + if(aDevice.getStartupActions() != null && !aDevice.getStartupActions().isEmpty()) { + log.info("Startup call for {} with startupActions {}", aDevice.getName(), aDevice.getStartupActions()); + aChangeBody = "{"; + components = aDevice.getStartupActions().split(":"); + if(components.length > 0 && components[0] != null && components[0].length() > 0) { + if(components[0].equals("On")) { + aChangeBody = aChangeBody + "\"on\":true"; + } + else { + aChangeBody = aChangeBody + "\"on\":false"; + } + comma = true; + } + if(components.length > 1 && components[1] != null && components[1].length() > 0 && !(components.length > 2 && components[2] != null && components[2].length() > 0)) { + if(comma) + aChangeBody = aChangeBody + ","; + aChangeBody = aChangeBody + "\"bri\":" + components[1]; + comma = true; + } + if(components.length > 2 && components[2] != null && components[2].length() > 0) { + if(comma) + aChangeBody = aChangeBody + ","; + String theRGB = components[2].substring(components[2].indexOf('(') + 1, components[2].indexOf(')')); + String[] RGB = theRGB.split(","); + float[] hsb = new float[3]; + Color.RGBtoHSB(Integer.parseInt(RGB[0]), Integer.parseInt(RGB[1]), Integer.parseInt(RGB[2]), hsb); + float hue = hsb[0] * (float) 360.0; + float sat = hsb[1] * (float) 100.0; + float bright = hsb[2] * (float) 100.0; + aChangeBody = String.format("%s\"hue\":%.2f,\"sat\":%.2f,\"bri\":%d", aChangeBody, hue, sat, Math.round(bright)); + } + aChangeBody = aChangeBody + "}"; + log.info("Startup call to set state for {} with body {}", aDevice.getName(), aChangeBody); + changeState(aUserId, aDevice.getId(), aChangeBody, "localhost", true); + } + } + } } diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index 5b861ff..7c59837 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -1279,7 +1279,7 @@ app.service('bridgeService', function ($rootScope, $http, $base64, $location, ng ); }; - this.downloadBackup = function (afilename) { + this.downloadDeviceBackup = function (afilename) { return $http.put(this.state.base + "/backup/download", { filename: afilename }).then( @@ -1459,6 +1459,54 @@ app.service('bridgeService', function ($rootScope, $http, $base64, $location, ng ); }; + this.downloadConfigBackup = function (afilename) { + return $http.put(this.state.systemsbase + "/backup/download", { + filename: afilename + }).then( + function (response) { + self.state.backupContent = response.data; + var blob = new Blob([self.state.backupContent], { + type: 'text/plain' + }); + var downloadLink = angular.element(''); + downloadLink.attr('href', window.URL.createObjectURL(blob)); + downloadLink.attr('download', afilename); + downloadLink[0].click(); + }, + function (error) { + if (error.status === 401) + $rootScope.$broadcast('securityReinit', 'done'); + else + self.displayWarn("Download Backup Config File Error:", error); + } + ); + }; + + this.uploadConfigFile = function (filename, file) { + file.upload = Upload.http({ + url: this.state.systemsbase + "/backup/upload/" + filename, + method: 'PUT', + headers: { + 'Content-Type': file.type + }, + data: file + }); + + file.upload.then(function (response) { + file.result = response.data; + self.viewConfigs(); + }, function (response) { + if (response.status === 401) + $rootScope.$broadcast('securityReinit', 'done'); + else if (response.status > 0) + self.displayWarn('Upload Backup Config File Error:' + response.status + ': ' + response.data); + }); + + file.upload.progress(function (evt) { + file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)); + }); + } + this.deleteDevice = function (id) { return $http.delete(this.state.base + "/" + id).then( function (response) { @@ -2074,6 +2122,17 @@ app.controller('SystemController', function ($scope, $location, bridgeService, n $scope.deleteSettingsBackup = function (backupname) { bridgeService.deleteSettingsBackup(backupname); }; + + $scope.downloadBackup = function (backupname) { + bridgeService.downloadConfigBackup(backupname); + }; + + $scope.uploadConfigFile = function (aFilename, aConfigFile) { + if (aConfigFile != null) { + bridgeService.uploadConfigFile(aFilename, aConfigFile); + } + }; + $scope.toggle = function () { $scope.visible = !$scope.visible; if ($scope.visible) @@ -2349,12 +2408,41 @@ app.controller('ViewingController', function ($scope, $location, bridgeService, bridgeService.editDevice(device); $location.path('/editdevice'); }; + $scope.setStartupAction = function(device) { + $scope.bridge.device = device; + ngDialog.open({ + template: 'startupActionDialog', + controller: 'StartupActionDialogCtrl', + className: 'ngdialog-theme-default' + }); + }; + $scope.renumberDevices = function () { bridgeService.renumberDevices(); }; $scope.pushLinkButton = function () { bridgeService.pushLinkButton(); }; + + $scope.toggleLock = function (device) { + if(device.lockDeviceId) { + device.lockDeviceId = false; + } else { + device.lockDeviceId = true; + } + console.log("toggle lock device called: " + device.name); + bridgeService.addDevice(device).then( + function () { + bridgeService.state.queueDevId = device.id; + console.log("Device updated for Q Id <<" + bridgeService.state.queueDevId + ">>"); + $location.path('/'); + }, + function (error) { + bridgeService.displayWarn("Error updating lock id for device....", error); + } + ); + }; + $scope.manageLinksButton = function () { ngDialog.open({ template: 'views/managelinksdialog.html', @@ -2372,7 +2460,7 @@ app.controller('ViewingController', function ($scope, $location, bridgeService, bridgeService.deleteBackup(backupname); }; $scope.downloadBackup = function (backupname) { - bridgeService.downloadBackup(backupname); + bridgeService.downloadDeviceBackup(backupname); }; $scope.uploadDeviceFile = function (aFilename, aDeviceFile) { @@ -2408,23 +2496,23 @@ app.controller('ViewingController', function ($scope, $location, bridgeService, }); app.controller('ValueDialogCtrl', function ($scope, bridgeService, ngDialog) { + $scope.bridge = bridgeService.state; + $scope.valueType = "percentage"; $scope.slider = { - value: 100, + value: Math.round($scope.bridge.device.deviceState.bri / 2.55), options: { floor: 1, ceil: 100, showSelectionBar: true } }; - $scope.bridge = bridgeService.state; - $scope.valueType = "percentage"; $scope.changeScale = function () { if ($scope.valueType === "raw") { $scope.slider.options.ceil = 254; - $scope.slider.value = 254; + $scope.slider.value = $scope.bridge.device.deviceState.bri; } else { $scope.slider.options.ceil = 100; - $scope.slider.value = 100; + $scope.slider.value = Math.round($scope.bridge.device.deviceState.bri / 2.55); } }; $scope.setValue = function () { @@ -2473,6 +2561,57 @@ app.controller('DeleteDialogCtrl', function ($scope, bridgeService, ngDialog) { }; }); +app.controller('StartupActionDialogCtrl', function ($scope, bridgeService, ngDialog) { + $scope.bridge = bridgeService.state; + $scope.device = $scope.bridge.device; + $scope.setDim = false; + var components = []; + if($scope.device.startupActions != undefined) { + components = $scope.device.startupActions.split(":"); + if(components[1] != undefined && components[1].length > 0) + $scope.setDim = true; + } else { + components = "::".split(":"); + } + + $scope.slider = { + value: parseInt(components[1]), + options: { + floor: 1, + ceil: 254, + showSelectionBar: true + } + }; + $scope.rgbPicker = { + color: components[2] + }; + + $scope.theState = components[0]; + + $scope.startupActionSave = function (device) { + console.log("Startup action set for device called: " + device.name); + ngDialog.close('ngdialog1'); + var theValue = 1; + if($scope.setDim) { + theValue = $scope.theState + ":" + $scope.slider.value + ":" + $scope.rgbPicker.color; + } else { + theValue = $scope.theState + "::" + $scope.rgbPicker.color; + } + if(theValue == "::") + theValue = ""; + device.startupActions = theValue; + bridgeService.addDevice(device).then( + function () { + bridgeService.state.queueDevId = device.id; + console.log("Device updated for Q Id <<" + bridgeService.state.queueDevId + ">>"); + }, + function (error) { + bridgeService.displayWarn("Error updating lock id for device....", error); + } + ); + }; +}); + app.controller('VeraController', function ($scope, $location, bridgeService, ngDialog) { $scope.bridge = bridgeService.state; $scope.device = bridgeService.state.device; diff --git a/src/main/resources/public/views/configuration.html b/src/main/resources/public/views/configuration.html index c51acbe..a65d57f 100644 --- a/src/main/resources/public/views/configuration.html +++ b/src/main/resources/public/views/configuration.html @@ -68,9 +68,9 @@ ID Name Description - Device State Type Target + Startup Action Inactive No State Actions @@ -79,14 +79,17 @@ {{$index+1}} - {{device.id}} - {{device.name}} - {{device.description}} - - on={{device.deviceState.on}},bri={{device.deviceState.bri}},hue={{device.deviceState.hue}},sat={{device.deviceState.sat}},effect={{device.deviceState.effect}},ct={{device.deviceState.ct}},alert={{device.deviceState.alert}},colormode={{device.deviceState.colormode}},reachable={{device.deviceState.reachable}},XYList={{device.deviceState.xy}} + + {{device.id}} +

{{device.id}}

+ + {{device.name}} + {{device.description}} {{device.deviceType}} {{device.targetDevice}} + {{device.startupActions}} {{device.inactive}} {{device.noState}} @@ -139,13 +142,13 @@ - - -
-
- Upload Successful + +
+
+
+ Upload Successful @@ -201,4 +204,44 @@
- \ No newline at end of file + + \ No newline at end of file diff --git a/src/main/resources/public/views/system.html b/src/main/resources/public/views/system.html index 5a3b5ec..df98856 100644 --- a/src/main/resources/public/views/system.html +++ b/src/main/resources/public/views/system.html @@ -985,6 +985,22 @@ Settings + +
+ +
+ +
+ + + +
+
+ Upload Successful +
+
@@ -993,7 +1009,7 @@ - +
{{backup}}{{backup}}