Skip to content

Commit

Permalink
Backup and restore network (#1439)
Browse files Browse the repository at this point in the history
* Implement full network backup / restore functionality

Signed-off-by: Chris Jackson <chris@cd-jackson.com>

* Update to set IEEE address in Ember NCP

Signed-off-by: Chris Jackson <chris@cd-jackson.com>

* Update following testing

Signed-off-by: Chris Jackson <chris@cd-jackson.com>

* Fix log statement

Signed-off-by: Chris Jackson <chris@cd-jackson.com>

* Minor updates following comments

---------

Signed-off-by: Chris Jackson <chris@cd-jackson.com>
  • Loading branch information
cdjackson authored Dec 7, 2024
1 parent 5774fca commit 116700a
Show file tree
Hide file tree
Showing 19 changed files with 953 additions and 357 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.OutputStreamWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -27,6 +28,7 @@
import com.zsmartsystems.zigbee.database.ZclAttributeDao;
import com.zsmartsystems.zigbee.database.ZclClusterDao;
import com.zsmartsystems.zigbee.database.ZigBeeEndpointDao;
import com.zsmartsystems.zigbee.database.ZigBeeNetworkBackupDao;
import com.zsmartsystems.zigbee.database.ZigBeeNetworkDataStore;
import com.zsmartsystems.zigbee.database.ZigBeeNodeDao;
import com.zsmartsystems.zigbee.security.ZigBeeKey;
Expand All @@ -47,19 +49,25 @@ public class ZigBeeDataStore implements ZigBeeNetworkDataStore {
*/
private final static Logger logger = LoggerFactory.getLogger(ZigBeeDataStore.class);

private final static String CHARSET = "UTF-8";
private final static String DATABASE = "database/";
private final static String KEYSTORE = "keystore";
private final static String BACKUP = "backup";

private final String networkId;

public ZigBeeDataStore(String networkId) {
this.networkId = "database/" + networkId + "/";
File file = new File(this.networkId + "/" + KEYSTORE);
if (file.exists()) {
return;
}
if (!file.mkdirs()) {
this.networkId = DATABASE + networkId + "/";
File file;

file = new File(this.networkId + "/" + KEYSTORE);
if (!file.exists() && !file.mkdirs()) {
logger.error("Error creating network database folder {}", file);
}
file = new File(DATABASE + BACKUP);
if (!file.exists() && !file.mkdirs()) {
logger.error("Error creating network backup folder {}", file);
}
}

private XStream openStream() {
Expand Down Expand Up @@ -99,6 +107,10 @@ private File getFile(IeeeAddress address) {
return new File(networkId + address + ".xml");
}

private File getFile(UUID uuid) {
return new File(DATABASE + BACKUP + "/" + uuid + ".xml");
}

private File getFile(String key) {
return new File(networkId + KEYSTORE + "/" + key + ".xml");
}
Expand Down Expand Up @@ -135,10 +147,9 @@ public ZigBeeNodeDao readNode(IeeeAddress address) {
File file = getFile(address);

ZigBeeNodeDao node = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), CHARSET))) {
node = (ZigBeeNodeDao) stream.fromXML(reader);
reader.close();
logger.info("{}: ZigBee reading network state complete.", address);
} catch (Exception e) {
logger.error("{}: Error reading network state: ", address, e);
}
Expand All @@ -151,10 +162,9 @@ public void writeNode(ZigBeeNodeDao node) {
XStream stream = openStream();
File file = getFile(node.getIeeeAddress());

try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {
stream.marshal(node, new PrettyPrintWriter(writer));
writer.close();
logger.info("{}: ZigBee saving network state complete.", node.getIeeeAddress());
} catch (Exception e) {
logger.error("{}: Error writing network state: ", node.getIeeeAddress(), e);
}
Expand All @@ -173,10 +183,9 @@ public void writeObject(String key, Object object) {
XStream stream = openStream();
File file = getFile(key);

try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {
stream.marshal(object, new PrettyPrintWriter(writer));
writer.close();
logger.info("{}: ZigBee saving key complete.", key);
} catch (Exception e) {
logger.error("{}: Error writing key: ", key, e);
}
Expand All @@ -187,4 +196,67 @@ public Object readObject(String key) {
return null;
}

@Override
public boolean writeBackup(ZigBeeNetworkBackupDao backup) {
XStream stream = openStream();
File file = getFile(backup.getUuid());

try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {
stream.marshal(backup, new PrettyPrintWriter(writer));
writer.close();
} catch (Exception e) {
logger.error("{}: Error writing network backup: ", backup.getUuid(), e);
return false;
}

return true;
}

@Override
public ZigBeeNetworkBackupDao readBackup(UUID uuid) {
XStream stream = openStream();
File file = getFile(uuid);

ZigBeeNetworkBackupDao backup = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), CHARSET))) {
backup = (ZigBeeNetworkBackupDao) stream.fromXML(reader);
reader.close();
} catch (Exception e) {
logger.error("{}: Error reading network backup: ", uuid, e);
}

return backup;
}

@Override
public Set<ZigBeeNetworkBackupDao> listBackups() {
Set<ZigBeeNetworkBackupDao> backups = new HashSet<>();
File dir = new File(DATABASE + BACKUP);
File[] files = dir.listFiles();

if (files == null) {
return backups;
}

for (File file : files) {
if (!file.getName().toLowerCase().endsWith(".xml")) {
continue;
}

try {
String filename = file.getName();
UUID uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
ZigBeeNetworkBackupDao backup = readBackup(uuid);
for (ZigBeeNodeDao node : backup.getNodes()) {
node.setEndpoints(null);
node.setBindingTable(null);
}
backups.add(backup);
} catch (IllegalArgumentException e) {
logger.error("Error parsing database filename: {}", file.getName());
}
}

return backups;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public String getCommand() {

@Override
public String getDescription() {
return "Sets the link key int the dongle, optionally computing the MMO Hash from the join code";
return "Sets the link key in the dongle, optionally computing the MMO Hash from the join code";
}

@Override
Expand Down
Loading

0 comments on commit 116700a

Please sign in to comment.