diff --git a/pom.xml b/pom.xml
index 52171e7..325ae97 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.bwssystems.HABridge
ha-bridge
- 1.3.1a
+ 1.3.1b
jar
HA Bridge
diff --git a/src/main/java/com/bwssystems/HABridge/dao/BackupFilename.java b/src/main/java/com/bwssystems/HABridge/dao/BackupFilename.java
new file mode 100644
index 0000000..f1c5cdb
--- /dev/null
+++ b/src/main/java/com/bwssystems/HABridge/dao/BackupFilename.java
@@ -0,0 +1,13 @@
+package com.bwssystems.HABridge.dao;
+
+public class BackupFilename {
+ private String filename;
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+}
diff --git a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java
index 8abbc20..4a90243 100644
--- a/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java
+++ b/src/main/java/com/bwssystems/HABridge/dao/DeviceRepository.java
@@ -3,12 +3,17 @@ package com.bwssystems.HABridge.dao;
import java.io.IOException;
import java.io.StringReader;
+import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@@ -33,8 +38,16 @@ public class DeviceRepository {
public DeviceRepository(String deviceDb) {
super();
- repositoryPath = Paths.get(deviceDb);
- String jsonContent = repositoryReader(repositoryPath);
+ _loadRepository(deviceDb);
+ }
+
+ private void _loadRepository(String aFilePath){
+ repositoryPath = Paths.get(aFilePath);
+ _loadRepository(repositoryPath);
+ }
+
+ private void _loadRepository(Path aPath){
+ String jsonContent = repositoryReader(aPath);
devices = new HashMap();
if(jsonContent != null)
@@ -47,7 +60,8 @@ public class DeviceRepository {
put(theDevice.getId(), theDevice);
}
}
- }
+
+ }
public List findAll() {
List list = new ArrayList(devices.values());
@@ -79,6 +93,66 @@ public class DeviceRepository {
log.debug("Save device: " + aDescriptor.getName());
}
+ public String backup(String aFilename) {
+ if(aFilename == null || aFilename.equalsIgnoreCase("")) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+ aFilename = "devicedb-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk";
+ }
+ else
+ aFilename = aFilename + ".bk";
+ try {
+ Files.copy(repositoryPath, FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), StandardCopyOption.COPY_ATTRIBUTES);
+ } catch (IOException e) {
+ log.error("Could not backup to file: " + aFilename + " message: " + e.getMessage(), e);
+ }
+ log.debug("Backup repository: " + aFilename);
+ return aFilename;
+ }
+
+ public String deleteBackup(String aFilename) {
+ log.debug("Delete backup repository: " + aFilename);
+ try {
+ Files.delete(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename));
+ } catch (IOException e) {
+ log.error("Could not delete file: " + aFilename + " message: " + e.getMessage(), e);
+ }
+ return aFilename;
+ }
+
+ public String restoreBackup(String aFilename) {
+ log.debug("Restore backup repository: " + aFilename);
+ try {
+ Path target = null;
+ if(Files.exists(repositoryPath)) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+ target = FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), "devicedb-" + dateFormat.format(Calendar.getInstance().getTime()) + ".bk");
+ Files.move(repositoryPath, target);
+ }
+ Files.copy(FileSystems.getDefault().getPath(repositoryPath.getParent().toString(), aFilename), repositoryPath, StandardCopyOption.COPY_ATTRIBUTES);
+ } catch (IOException e) {
+ log.error("Error restoring the file: " + aFilename + " message: " + e.getMessage(), e);
+ return null;
+ }
+ _loadRepository(repositoryPath);
+ return aFilename;
+ }
+
+ public List getBackups() {
+ List theFilenames = new ArrayList();
+ Path dir = repositoryPath.getParent();
+ try (DirectoryStream stream =
+ Files.newDirectoryStream(dir, "*.{bk}")) {
+ for (Path entry: stream) {
+ theFilenames.add(entry.getFileName().toString());
+ }
+ } catch (IOException x) {
+ // IOException can never be thrown by the iteration.
+ // In this snippet, it can // only be thrown by newDirectoryStream.
+ log.error("Issue getting direcotyr listing for backups - " + x.getMessage());
+ }
+ return theFilenames;
+ }
+
public String delete(DeviceDescriptor aDescriptor) {
if (aDescriptor != null) {
devices.remove(aDescriptor.getId());
diff --git a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java
index 7e53de6..43bfb1f 100644
--- a/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java
+++ b/src/main/java/com/bwssystems/HABridge/devicemanagmeent/DeviceResource.java
@@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.JsonTransformer;
import com.bwssystems.HABridge.Version;
+import com.bwssystems.HABridge.dao.BackupFilename;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.dao.DeviceRepository;
import com.bwssystems.NestBridge.NestHome;
@@ -234,5 +235,66 @@ public class DeviceResource {
response.status(HttpStatus.SC_OK);
return nestHome.getItems();
}, new JsonTransformer());
+
+ get (API_CONTEXT + "/backup/available", "application/json", (request, response) -> {
+ log.debug("Get backup filenames");
+ response.status(HttpStatus.SC_OK);
+ return deviceRepository.getBackups();
+ }, new JsonTransformer());
+
+ // http://ip_address:port/api/devices/backup/create CORS request
+ options(API_CONTEXT + "/backup/create", "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 (API_CONTEXT + "/backup/create", "application/json", (request, response) -> {
+ log.debug("Create backup: " + request.body());
+ BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
+ BackupFilename returnFilename = new BackupFilename();
+ returnFilename.setFilename(deviceRepository.backup(aFilename.getFilename()));
+ return returnFilename;
+ }, new JsonTransformer());
+
+ // http://ip_address:port/api/devices/backup/delete CORS request
+ options(API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
+ response.status(HttpStatus.SC_OK);
+ response.header("Access-Control-Allow-Origin", request.headers("Origin"));
+ response.header("Access-Control-Allow-Methods", "POST");
+ response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
+ response.header("Content-Type", "text/html; charset=utf-8");
+ return "";
+ });
+ post (API_CONTEXT + "/backup/delete", "application/json", (request, response) -> {
+ log.debug("Delete backup: " + request.body());
+ BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
+ if(aFilename != null)
+ deviceRepository.deleteBackup(aFilename.getFilename());
+ else
+ log.warn("No filename given for delete backup.");
+ return null;
+ }, new JsonTransformer());
+
+ // http://ip_address:port/api/devices/backup/restore CORS request
+ options(API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
+ response.status(HttpStatus.SC_OK);
+ response.header("Access-Control-Allow-Origin", request.headers("Origin"));
+ response.header("Access-Control-Allow-Methods", "POST");
+ response.header("Access-Control-Allow-Headers", request.headers("Access-Control-Request-Headers"));
+ response.header("Content-Type", "text/html; charset=utf-8");
+ return "";
+ });
+ post (API_CONTEXT + "/backup/restore", "application/json", (request, response) -> {
+ log.debug("Restore backup: " + request.body());
+ BackupFilename aFilename = new Gson().fromJson(request.body(), BackupFilename.class);
+ if(aFilename != null)
+ deviceRepository.restoreBackup(aFilename.getFilename());
+ else
+ log.warn("No filename given for restore backup.");
+ return null;
+ }, new JsonTransformer());
}
}
\ No newline at end of file
diff --git a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
index 965d00b..8a34707 100644
--- a/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
+++ b/src/main/java/com/bwssystems/HABridge/hue/HueMulator.java
@@ -348,7 +348,7 @@ public class HueMulator {
else {
for(int i = 0; i < deviceButtons.length; i++) {
if( i > 0)
- Thread.sleep(500);
+ Thread.sleep(100);
log.debug("pressing button: " + deviceButtons[i].getDevice() + " - " + deviceButtons[i].getButton() + " - iteration: " + String.valueOf(i));
myHarmony.pressButton(deviceButtons[i]);
}
diff --git a/src/main/resources/public/scripts/app.js b/src/main/resources/public/scripts/app.js
index e774eda..8563ddc 100644
--- a/src/main/resources/public/scripts/app.js
+++ b/src/main/resources/public/scripts/app.js
@@ -94,7 +94,7 @@ app.factory('BridgeSettings', function() {
app.service('bridgeService', function ($http, $window, BridgeSettings) {
var self = this;
self.BridgeSettings = BridgeSettings;
- this.state = {base: window.location.origin + "/api/devices", upnpbase: window.location.origin + "/upnp/settings", huebase: window.location.origin + "/api", devices: [], device: [], error: "", showVera: false, showHarmony: false, showNest: false, habridgeversion: ""};
+ this.state = {base: window.location.origin + "/api/devices", upnpbase: window.location.origin + "/upnp/settings", huebase: window.location.origin + "/api", backups: [], devices: [], device: [], error: "", showVera: false, showHarmony: false, showNest: false, habridgeversion: ""};
this.viewDevices = function () {
this.state.error = "";
@@ -192,11 +192,26 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
);
};
+ this.viewBackups = function () {
+ this.state.error = "";
+ return $http.get(this.state.base + "/backup/available").then(
+ function (response) {
+ self.state.backups = response.data;
+ },
+ function (error) {
+ if (error.data) {
+ $window.alert("Get Backups Error: " + error.data.message);
+ } else {
+ $window.alert("Get Backups Error: unknown");
+ }
+ }
+ );
+ };
+
this.viewNestItems = function () {
this.state.error = "";
if(!this.state.showNest)
return;
- this.state.error = "";
return $http.get(this.state.base + "/nest/items").then(
function (response) {
self.state.nestitems = response.data;
@@ -215,7 +230,6 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
this.state.error = "";
if(!this.state.showVera)
return;
- this.state.error = "";
return $http.get(this.state.base + "/vera/devices").then(
function (response) {
self.state.veradevices = response.data;
@@ -361,6 +375,58 @@ app.service('bridgeService', function ($http, $window, BridgeSettings) {
}
};
+ this.backupDeviceDb = function (afilename) {
+ this.state.error = "";
+ return $http.put(this.state.base + "/backup/create", {
+ filename: afilename
+ }).then(
+ function (response) {
+ self.viewBackups();
+ },
+ function (error) {
+ if (error.data) {
+ self.state.error = error.data.message;
+ }
+ $window.alert("Backup Device Db Error: unknown");
+ }
+ );
+ };
+
+ this.restoreBackup = function (afilename) {
+ this.state.error = "";
+ return $http.post(this.state.base + "/backup/restore", {
+ filename: afilename
+ }).then(
+ function (response) {
+ self.viewBackups();
+ self.viewDevices();
+ },
+ function (error) {
+ if (error.data) {
+ self.state.error = error.data.message;
+ }
+ $window.alert("Backup Db Restore Error: unknown");
+ }
+ );
+ };
+
+ this.deleteBackup = function (afilename) {
+ this.state.error = "";
+ return $http.post(this.state.base + "/backup/delete", {
+ filename: afilename
+ }).then(
+ function (response) {
+ self.viewBackups();
+ },
+ function (error) {
+ if (error.data) {
+ self.state.error = error.data.message;
+ }
+ $window.alert("Backup Db Frlryr Error: unknown");
+ }
+ );
+ };
+
this.deleteDevice = function (id) {
this.state.error = "";
return $http.delete(this.state.base + "/" + id).then(
@@ -417,9 +483,13 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
$scope.BridgeSettings = bridgeService.BridgeSettings;
bridgeService.viewDevices();
+ bridgeService.viewBackups();
$scope.bridge = bridgeService.state;
+ $scope.optionalbackupname = "";
$scope.visible = false;
$scope.imgUrl = "glyphicon glyphicon-plus";
+ $scope.visibleBk = false;
+ $scope.imgBkUrl = "glyphicon glyphicon-plus";
$scope.predicate = '';
$scope.reverse = true;
$scope.order = function(predicate) {
@@ -443,6 +513,15 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
bridgeService.editDevice(device);
$location.path('/editdevice');
};
+ $scope.backupDeviceDb = function (optionalbackupname) {
+ bridgeService.backupDeviceDb(optionalbackupname);
+ };
+ $scope.restoreBackup = function (backupname) {
+ bridgeService.restoreBackup(backupname);
+ };
+ $scope.deleteBackup = function (backupname) {
+ bridgeService.deleteBackup(backupname);
+ };
$scope.toggle = function () {
$scope.visible = !$scope.visible;
if($scope.visible)
@@ -450,6 +529,13 @@ app.controller('ViewingController', function ($scope, $location, $http, $window,
else
$scope.imgUrl = "glyphicon glyphicon-plus";
};
+ $scope.toggleBk = function () {
+ $scope.visibleBk = !$scope.visibleBk;
+ if($scope.visibleBk)
+ $scope.imgBkUrl = "glyphicon glyphicon-minus";
+ else
+ $scope.imgBkUrl = "glyphicon glyphicon-plus";
+ };
});
app.controller('AddingController', function ($scope, $location, $http, bridgeService, BridgeSettings) {
diff --git a/src/main/resources/public/views/configuration.html b/src/main/resources/public/views/configuration.html
index aaac8dc..e7f8813 100644
--- a/src/main/resources/public/views/configuration.html
+++ b/src/main/resources/public/views/configuration.html
@@ -134,3 +134,40 @@
+
+
+
Bridge Device DB Backup
+
+
+
+
+
+
+ | Filename |
+ Actions |
+
+
+
+ | {{backup}} |
+
+
+
+ |
+
+
+
+
+
\ No newline at end of file