From 9c3d95f17748802d67478a662ffe13fb3778c804 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 21 Mar 2016 16:37:13 -0500 Subject: [PATCH] Finished implementing and testing state change and bug fixes from testing Fixes #44 Fixes #60 Fixes #61 Fixes #63 Fixes #66 Fixes #69 --- README.md | 24 ++++++++ pom.xml | 2 +- .../bwssystems/HABridge/SystemControl.java | 2 +- .../HABridge/dao/DeviceRepository.java | 3 +- .../bwssystems/HABridge/hue/HueMulator.java | 55 +++++++++++++++++++ src/main/resources/public/scripts/app.js | 23 +++++--- 6 files changed, 99 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8e3fc4b..a16cf8d 100644 --- a/README.md +++ b/README.md @@ -531,6 +531,30 @@ A response to a successful PUT request contains confirmation of the arguments pa {"success":{"/lights/1/state/on":true}}, ] ``` +### Update bridge internal light state +Allows the user to set the internal state of the light on and off, modify the brightness. This is not a HUE API call and is special to the bridge as it keeps track of the state changes to the light from the api. It is intended to allow you to sync the bridge state with your HA system state. +``` +PUT http://host:port/api//lights//bridgeupdatestate +``` +#### Body arguments +Name | Type | Description +-----|-------|------------- +on | bool | On/Off state of the light. On=true, Off=false. Optional +bri | uint8 | The brightness value to set the light to. Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. e.g. "brightness": 60 will set the light to a specific brightness. Optional +``` +{ + "on": true, + "bri": 200 +} +``` +#### Response +A response to a successful PUT request contains confirmation of the arguments passed in. Note: If the new value is too large to return in the response due to internal memory constraints then a value of "Updated." is returned. +``` +[ + {"success":{"/lights/1/state/bri":200}}, + {"success":{"/lights/1/state/on":true}}, +] +``` ### Create user Emulates creating a new user. The link button state on the HA Bridge is always on for the purpose of responding to this request. No actual user is saved as this is for compatibility. ``` diff --git a/pom.xml b/pom.xml index 65070e5..92380cf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.bwssystems.HABridge ha-bridge - 1.4.1d + 1.4.2 jar HA Bridge diff --git a/src/main/java/com/bwssystems/HABridge/SystemControl.java b/src/main/java/com/bwssystems/HABridge/SystemControl.java index dbba63c..abcf7c6 100644 --- a/src/main/java/com/bwssystems/HABridge/SystemControl.java +++ b/src/main/java/com/bwssystems/HABridge/SystemControl.java @@ -240,7 +240,7 @@ public class SystemControl { } else log.warn("No filename given for restore backup."); - return null; + return bridgeSettings.getBridgeSettingsDescriptor(); }, new JsonTransformer()); } diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java index 06e5a8d..6dc7218 100644 --- a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java +++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java @@ -36,6 +36,7 @@ public class DeviceRepository extends BackupHandler { super(); gson = new GsonBuilder() + .excludeFieldsWithoutExposeAnnotation() .create(); repositoryPath = null; repositoryPath = Paths.get(deviceDb); @@ -81,7 +82,7 @@ public class DeviceRepository extends BackupHandler { public void save(DeviceDescriptor[] descriptors) { String theNames = ""; for(int i = 0; i < descriptors.length; i++) { - if(descriptors[i].getId() != null) + if(descriptors[i].getId() != null && descriptors[i].getId().length() > 0) devices.remove(descriptors[i].getId()); else descriptors[i].setId(String.valueOf(random.nextInt(Integer.MAX_VALUE))); diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java index de23c2c..1ac8d56 100644 --- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java +++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java @@ -256,6 +256,61 @@ public class HueMulator { return lightResponse; }, new JsonTransformer()); + // http://ip_address:port/api/:userid/lights/:id/bridgeupdatestate CORS request + options(HUE_CONTEXT + "/:userid/lights/:id/bridgeupdatestate", "application/json", (request, response) -> { + response.status(HttpStatus.SC_OK); + response.header("Access-Control-Allow-Origin", request.headers("Origin")); + response.header("Access-Control-Allow-Methods", "GET, POST, PUT"); + response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers")); + response.header("Content-Type", "text/html; charset=utf-8"); + return ""; + }); + // http://ip_address:port/api/{userId}/lights/{lightId}/bridgeupdatestate uses json object to update the internal bridge lights state. + // THIS IS NOT A HUE API CALL... It is for state management if so desired. + put(HUE_CONTEXT + "/:userid/lights/:id/bridgeupdatestate", "application/json", (request, response) -> { + String userId = request.params(":userid"); + String lightId = request.params(":id"); + String responseString = null; + DeviceState state = null; + log.debug("Update state requested: " + userId + " from " + request.ip() + " body: " + request.body()); + response.header("Access-Control-Allow-Origin", request.headers("Origin")); + response.type("application/json; charset=utf-8"); + response.status(HttpStatus.SC_OK); + try { + state = mapper.readValue(request.body(), DeviceState.class); + } catch (IOException e) { + log.warn("Object mapper barfed on input of body.", e); + responseString = "[{\"error\":{\"type\": 2, \"address\": \"/lights/" + lightId + "\",\"description\": \"Object mapper barfed on input of body.\"}}]"; + return responseString; + } + DeviceDescriptor device = repository.findOne(lightId); + if (device == null) { + log.warn("Could not find device: " + lightId + " for hue state change request: " + userId + " from " + request.ip() + " body: " + request.body()); + responseString = "[{\"error\":{\"type\": 3, \"address\": \"/lights/" + lightId + "\",\"description\": \"Could not find device\", \"resource\": \"/lights/" + lightId + "\"}}]"; + return responseString; + } + + responseString = "[{\"success\":{\"/lights/" + lightId + "/state/on\":"; + if(request.body().contains("bri")) + { + responseString = responseString + "true}},{\"success\":{\"/lights/" + lightId + "/state/bri\":" + state.getBri() + "}}]"; + } + else + { + if (state.isOn()) { + responseString = responseString + "true}}]"; + state.setBri(255); + } else if (request.body().contains("false")) { + responseString = responseString + "false}}]"; + state.setBri(0); + } + } + device.setDeviceSetValue(state.getBri()); + device.setDeviceState(state.isOn()); + + return responseString; + }); + // http://ip_address:port/api/:userid/lights/:id/state CORS request options(HUE_CONTEXT + "/:userid/lights/:id/state", "application/json", (request, response) -> { response.status(HttpStatus.SC_OK); diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js index ef6f706..6d665e3 100644 --- a/src/main/resources/public/scripts/app.js +++ b/src/main/resources/public/scripts/app.js @@ -450,6 +450,7 @@ app.service('bridgeService', function ($http, $window, ngToast) { filename: afilename }).then( function (response) { + self.state.settings = response.data; self.viewConfigs(); self.viewDevices(); }, @@ -765,6 +766,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi }; $scope.buildDeviceUrls = function (veradevice, dim_control) { + bridgeService.clearDevice(); $scope.device.deviceType = "switch"; $scope.device.name = veradevice.name; $scope.device.targetDevice = veradevice.veraname; @@ -789,6 +791,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi }; $scope.buildSceneUrls = function (verascene) { + bridgeService.clearDevice(); $scope.device.deviceType = "scene"; $scope.device.name = verascene.name; $scope.device.targetDevice = verascene.veraname; @@ -813,6 +816,7 @@ app.controller('VeraController', function ($scope, $location, $http, bridgeServi bridgeService.viewVeraScenes(); }, function (error) { + bridgeService.displayWarn("Error adding device: " + $scope.device.name, error) } ); @@ -892,6 +896,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe }; $scope.buildActivityUrls = function (harmonyactivity) { + bridgeService.clearDevice(); $scope.device.deviceType = "activity"; $scope.device.targetDevice = harmonyactivity.hub; $scope.device.name = harmonyactivity.activity.label; @@ -902,6 +907,7 @@ app.controller('HarmonyController', function ($scope, $location, $http, bridgeSe }; $scope.buildButtonUrls = function (harmonydevice, onbutton, offbutton) { + bridgeService.clearDevice(); var currentOn = $scope.device.onUrl; var currentOff = $scope.device.offUrl; var actionOn = angular.fromJson(onbutton); @@ -966,6 +972,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestHomeUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "home"; $scope.device.name = nestitem.name; $scope.device.targetDevice = nestitem.name; @@ -976,6 +983,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestTempUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Temperature"; $scope.device.targetDevice = nestitem.location; @@ -986,6 +994,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestHeatUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Heat"; $scope.device.targetDevice = nestitem.location; @@ -996,6 +1005,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestCoolUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Cool"; $scope.device.targetDevice = nestitem.location; @@ -1006,6 +1016,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestRangeUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Range"; $scope.device.targetDevice = nestitem.location; @@ -1016,6 +1027,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestOffUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Thermostat"; $scope.device.targetDevice = nestitem.location; @@ -1026,6 +1038,7 @@ app.controller('NestController', function ($scope, $location, $http, bridgeServi }; $scope.buildNestFanUrls = function (nestitem) { + bridgeService.clearDevice(); $scope.device.deviceType = "thermo"; $scope.device.name = nestitem.name.substr(0, nestitem.name.indexOf("(")) + " Fan"; $scope.device.targetDevice = nestitem.location; @@ -1072,7 +1085,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi $scope.device_dim_control = ""; $scope.bulk = { devices: [] }; var veraList = angular.fromJson($scope.bridge.settings.veraaddress); - if(veraList != null && veraList.length > 0) + if(veraList != null && veraList.devices.length > 0) $scope.vera = {base: "http://" + veraList.devices[0].ip, port: "3480", id: ""}; else $scope.vera = {base: "http://", port: "3480", id: ""}; @@ -1084,9 +1097,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi }; $scope.buildUrlsUsingDevice = function (dim_control) { - if ($scope.vera.base.indexOf("http") < 0) { - $scope.vera.base = "http://" + $scope.vera.base; - } + bridgeService.clearDevice(); $scope.device.deviceType = "switch"; $scope.device.targetDevice = "Encapsulated"; $scope.device.mapType = "veraDevice"; @@ -1110,9 +1121,7 @@ app.controller('EditController', function ($scope, $location, $http, bridgeServi }; $scope.buildUrlsUsingScene = function () { - if ($scope.vera.base.indexOf("http") < 0) { - $scope.vera.base = "http://" + $scope.vera.base; - } + bridgeService.clearDevice(); $scope.device.deviceType = "scene"; $scope.device.targetDevice = "Encapsulated"; $scope.device.mapType = "veraScene";