diff --git a/com.zsmartsystems.zigbee.console.main/src/main/java/com/zsmartsystems/zigbee/console/main/ZigBeeDataStore.java b/com.zsmartsystems.zigbee.console.main/src/main/java/com/zsmartsystems/zigbee/console/main/ZigBeeDataStore.java index 9882919cc..ee518e8ae 100644 --- a/com.zsmartsystems.zigbee.console.main/src/main/java/com/zsmartsystems/zigbee/console/main/ZigBeeDataStore.java +++ b/com.zsmartsystems.zigbee.console.main/src/main/java/com/zsmartsystems/zigbee/console/main/ZigBeeDataStore.java @@ -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; @@ -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; @@ -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() { @@ -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"); } @@ -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); } @@ -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); } @@ -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); } @@ -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 listBackups() { + Set 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; + } } diff --git a/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleLinkKeyCommand.java b/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleLinkKeyCommand.java index 67f4f4d99..9d5aa9399 100644 --- a/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleLinkKeyCommand.java +++ b/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleLinkKeyCommand.java @@ -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 diff --git a/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleNetworkBackupCommand.java b/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleNetworkBackupCommand.java index 5427c9306..2dfc02adc 100644 --- a/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleNetworkBackupCommand.java +++ b/com.zsmartsystems.zigbee.console/src/main/java/com/zsmartsystems/zigbee/console/ZigBeeConsoleNetworkBackupCommand.java @@ -8,22 +8,17 @@ package com.zsmartsystems.zigbee.console; import java.io.PrintStream; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; -import com.zsmartsystems.zigbee.ExtendedPanId; -import com.zsmartsystems.zigbee.ZigBeeChannel; import com.zsmartsystems.zigbee.ZigBeeNetworkManager; -import com.zsmartsystems.zigbee.security.ZigBeeKey; -import com.zsmartsystems.zigbee.transport.DeviceType; +import com.zsmartsystems.zigbee.ZigBeeStatus; +import com.zsmartsystems.zigbee.database.ZigBeeNetworkBackupDao; +import com.zsmartsystems.zigbee.database.ZigBeeNodeDao; /** - * Console command to backup the network. This prints the important information required to backup the coordinator to a - * string which can then be parsed and set back into the same, or a different coordinator. - * Key counters are incremented with an assumption of 1 frame per second since the backup. + * Console command to backup the network. * * @author Chris Jackson * @@ -36,12 +31,12 @@ public String getCommand() { @Override public String getDescription() { - return "Backup or restore the coordinator."; + return "Backup or restore the network."; } @Override public String getSyntax() { - return "[RESTORE STRING]"; + return "[BACKUP] [RESTORE UUID] [LIST]"; } @Override @@ -52,153 +47,64 @@ public String getHelp() { @Override public void process(ZigBeeNetworkManager networkManager, String[] args, PrintStream out) throws IllegalArgumentException { - if (args.length > 2) { + if (args.length < 2) { throw new IllegalArgumentException("Invalid number of arguments"); } - if (args.length == 1) { - out.println(netBackup(networkManager)); - return; + switch (args[1].toUpperCase()) { + case "LIST": + listBackups(out, networkManager); + break; + case "BACKUP": + createBackup(out, networkManager); + break; + case "RESTORE": + restoreBackup(out, networkManager, UUID.fromString(args[2])); + break; + default: + throw new IllegalArgumentException("Unknown option '" + args[1] + "'"); } - - String[] parameters = args[1].split("\\>"); - netRestore(networkManager, parameters, out); } - private String netBackup(ZigBeeNetworkManager networkManager) { - TimeZone timezone = TimeZone.getTimeZone("UTC"); - DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); - formatter.setTimeZone(timezone); - String nowAsIso = formatter.format(new Date()); - - int pan = networkManager.getZigBeePanId(); - ExtendedPanId epan = networkManager.getZigBeeExtendedPanId(); - ZigBeeChannel channel = networkManager.getZigBeeChannel(); - ZigBeeKey networkKey = networkManager.getZigBeeNetworkKey(); - ZigBeeKey linkKey = networkManager.getZigBeeLinkKey(); - - StringBuilder builder = new StringBuilder(); - builder.append(nowAsIso); - builder.append('>'); - builder.append(DeviceType.COORDINATOR); // Future proof - builder.append('>'); - builder.append(String.format("%04X", pan)); - builder.append('>'); - builder.append(epan.toString()); - builder.append('>'); - builder.append(channel.toString()); - builder.append('>'); - builder.append(networkKey.toString()); - builder.append('>'); - if (networkKey.hasSequenceNumber()) { - builder.append(String.format("%02X", networkKey.getSequenceNumber())); - } - builder.append('>'); - if (networkKey.hasIncomingFrameCounter()) { - builder.append(String.format("%08X", networkKey.getIncomingFrameCounter())); - } - builder.append('>'); - if (networkKey.hasOutgoingFrameCounter()) { - builder.append(String.format("%08X", networkKey.getOutgoingFrameCounter())); - } - builder.append('>'); - builder.append(linkKey.toString()); - builder.append('>'); - if (linkKey.hasSequenceNumber()) { - builder.append(String.format("%02X", linkKey.getSequenceNumber())); - } - builder.append('>'); - if (linkKey.hasIncomingFrameCounter()) { - builder.append(String.format("%08X", linkKey.getIncomingFrameCounter())); - } - builder.append('>'); - if (linkKey.hasOutgoingFrameCounter()) { - builder.append(String.format("%08X", linkKey.getOutgoingFrameCounter())); - } - - return builder.toString(); - } - - private void netRestore(ZigBeeNetworkManager networkManager, String[] parameters, PrintStream out) - throws IllegalArgumentException { - int secondsSinceBackup; - try { - DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); - Date backupTime = formatter.parse(parameters[0]); - secondsSinceBackup = (int) ((new Date().getTime() - backupTime.getTime()) / 1000); - } catch (ParseException e) { - throw new IllegalArgumentException("Error parsing backup time"); - } - - int pan = Integer.parseInt(parameters[2], 16); - ExtendedPanId epan = new ExtendedPanId(parameters[3]); - ZigBeeChannel channel = ZigBeeChannel.valueOf(parameters[4]); - ZigBeeKey networkKey = parseKey(secondsSinceBackup, parameters[5], parameters[6], parameters[7], parameters[8]); - ZigBeeKey linkKey = parseKey(secondsSinceBackup, parameters[9], parameters[10], parameters[11], parameters[12]); - - out.println("Restoring network as " + parameters[1] + " after " + getTimeSince(secondsSinceBackup)); - out.println("PAN ID :"); - out.println("Extended PAN ID :"); - out.println("Channel :"); - out.println("Network Key :" + networkKey); - if (networkKey.hasSequenceNumber()) { - out.println("Network Key Sequence :" + networkKey.getSequenceNumber()); - } - if (networkKey.hasIncomingFrameCounter()) { - out.println("Network Key Incoming Counter :" + networkKey.getIncomingFrameCounter()); - } - if (networkKey.hasIncomingFrameCounter()) { - out.println("Network Key Outgoing Counter :" + networkKey.getOutgoingFrameCounter()); - } - out.println("Link Key :" + linkKey); - if (linkKey.hasSequenceNumber()) { - out.println("Link Key Sequence :" + linkKey.getSequenceNumber()); - } - if (linkKey.hasIncomingFrameCounter()) { - out.println("Link Key Incoming Counter :" + linkKey.getIncomingFrameCounter()); - } - if (linkKey.hasIncomingFrameCounter()) { - out.println("Link Key Outgoing Counter :" + linkKey.getOutgoingFrameCounter()); + private void listBackups(PrintStream out, ZigBeeNetworkManager networkManager) { + Map sortedBackups = new TreeMap<>(); + for (ZigBeeNetworkBackupDao backup : networkManager.listBackups()) { + sortedBackups.put(backup.getDate().getTime(), backup); + } + + out.println( + "DATE UUID PANID EPANID CHANNEL COORDINATOR NODES"); + for (ZigBeeNetworkBackupDao backup : sortedBackups.values()) { + ZigBeeNodeDao coordinator = null; + for (ZigBeeNodeDao node : backup.getNodes()) { + if (node.getNetworkAddress() == 0) { + coordinator = node; + break; + } + } + out.println( + String.format("%s %s %04X %s %s %s %d", backup.getDate().toInstant().toString(), + backup.getUuid(), + backup.getPan(), backup.getEpan(), backup.getChannel(), + (coordinator != null ? coordinator.getIeeeAddress() : " "), + backup.getNodes().size())); } - - networkManager.setZigBeePanId(pan); - networkManager.setZigBeeExtendedPanId(epan); - networkManager.setZigBeeChannel(channel); - networkManager.setZigBeeNetworkKey(networkKey); - networkManager.setZigBeeLinkKey(linkKey); - networkManager.startup(true); } - private ZigBeeKey parseKey(int secondsSinceBackup, String keyString, String sequence, String incount, - String outcount) { - ZigBeeKey key = new ZigBeeKey(keyString); - try { - if (!sequence.isEmpty()) { - key.setSequenceNumber(Integer.parseInt(sequence, 16)); - } - if (!incount.isEmpty()) { - key.setIncomingFrameCounter(Integer.parseInt(incount, 16) + secondsSinceBackup); - } - if (!outcount.isEmpty()) { - key.setOutgoingFrameCounter(Integer.parseInt(outcount, 16) + secondsSinceBackup); - } - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Error parsing key parameters"); + private void createBackup(PrintStream out, ZigBeeNetworkManager networkManager) { + UUID uuid = networkManager.createBackup(); + if (uuid == null) { + out.println("Error creating backup!!"); + } else { + out.println("Backup created with UUID " + uuid); } - - return key; } - private String getTimeSince(int secondsSince) { - if (secondsSince < 60) { - return secondsSince + " seconds"; - } - if (secondsSince < 3600) { - return secondsSince / 60 + " minutes"; - } - if (secondsSince < 86400) { - return secondsSince / 3600 + " hours"; + private void restoreBackup(PrintStream out, ZigBeeNetworkManager networkManager, UUID uuid) { + if (networkManager.restoreBackup(uuid) == ZigBeeStatus.SUCCESS) { + out.println("Backup restored from " + uuid.toString()); + } else { + out.println("Error restoring backup " + uuid.toString()); } - return secondsSince / 86400 + " days"; } } \ No newline at end of file diff --git a/com.zsmartsystems.zigbee.dongle.ember.autocode/src/main/resources/ezsp_protocol.xml b/com.zsmartsystems.zigbee.dongle.ember.autocode/src/main/resources/ezsp_protocol.xml index c524199a8..126b54c50 100644 --- a/com.zsmartsystems.zigbee.dongle.ember.autocode/src/main/resources/ezsp_protocol.xml +++ b/com.zsmartsystems.zigbee.dongle.ember.autocode/src/main/resources/ezsp_protocol.xml @@ -1964,26 +1964,6 @@ - - becomeTrustCenter - 0x77 - This function causes a coordinator to become the Trust Center when it is operating in a network that is not using one. It will send out an updated Network Key to all devices that will indicate a transition of the network to now use a Trust Center. The Trust Center should also switch all devices to using this new network key with the appropriate API. - - - EmberKeyData - newNetworkKey - The key data for the Updated Network Key. - - - - - EmberStatus - status - The response status. - - - - aesMmoHash 0x6F @@ -3916,6 +3896,30 @@ A command which does nothing. The Host can use this to set the sleep mode or check the status of the NCP. + + tokenFactoryReset + 0x0077 + Factory reset all configured Zigbee tokens. + + + bool + excludeOutgoingFC + Exclude network and APS outgoing frame counter tokens. + + + bool + excludeBootCounter + Exclude stack boot counter token. + + + + + + resetNode + 0x0104 + Reset the node by calling halReboot. + + EmberCertificateData The implicit certificate used in CBKE. diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/EmberNcp.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/EmberNcp.java index 2829d53ae..cd33c2365 100644 --- a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/EmberNcp.java +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/EmberNcp.java @@ -132,6 +132,8 @@ import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspSetValueResponse; import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspStartScanRequest; import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspStartScanResponse; +import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspTokenFactoryResetRequest; +import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspTokenFactoryResetResponse; import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspVersionRequest; import com.zsmartsystems.zigbee.dongle.ember.ezsp.command.EzspVersionResponse; import com.zsmartsystems.zigbee.dongle.ember.ezsp.structure.EmberAesMmoHashContext; @@ -1073,6 +1075,16 @@ public IeeeAddress getMfgCustomEui64() { return new IeeeAddress(response); } + /** + * Sets the custom EUI64 (long address) from the manufacturer information block on the NCP + * + * @param address {@link IeeeAddress} containing the custom address + * @return {@link EmberStatus} + */ + public EmberStatus setMfgCustomEui64(IeeeAddress address) { + return setMfgToken(EzspMfgTokenId.EZSP_MFG_CUSTOM_EUI_64, address.getValue()); + } + /** * Gets the install code stored in the NCP memory * @@ -1333,6 +1345,28 @@ public EzspStatus resetToFactoryDefaults() { return response.getStatus(); } + /** + * Factory reset all configured Zigbee tokens. + * + * @param excludeOutgoingFC Exclude network and APS outgoing frame counter tokens. + * @param excludeBootCounter Exclude stack boot counter token. + * @return the response {@link EzspStatus} + */ + public EzspStatus tokenFactoryReset(boolean excludeOutgoingFC, boolean excludeBootCounter) { + EzspTokenFactoryResetRequest request = new EzspTokenFactoryResetRequest(); + request.setExcludeOutgoingFC(excludeOutgoingFC); + request.setExcludeBootCounter(excludeBootCounter); + EzspTransaction transaction = protocolHandler + .sendEzspTransaction( + new EzspSingleResponseTransaction(request, EzspTokenFactoryResetResponse.class)); + EzspTokenFactoryResetResponse response = (EzspTokenFactoryResetResponse) transaction + .getResponse(); + if (response == null) { + return EzspStatus.UNKNOWN; + } + return EzspStatus.EZSP_SUCCESS; + } + private String intArrayToString(int[] payload) { int length = payload.length; for (int cnt = 0; cnt < length; cnt++) { diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ZigBeeDongleEzsp.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ZigBeeDongleEzsp.java index 214592eb5..b21c8cb4f 100644 --- a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ZigBeeDongleEzsp.java +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ZigBeeDongleEzsp.java @@ -30,6 +30,7 @@ import com.zsmartsystems.zigbee.ZigBeeDeviceType; import com.zsmartsystems.zigbee.ZigBeeExecutors; import com.zsmartsystems.zigbee.ZigBeeNetworkManager; +import com.zsmartsystems.zigbee.ZigBeeNetworkState; import com.zsmartsystems.zigbee.ZigBeeNodeStatus; import com.zsmartsystems.zigbee.ZigBeeNwkAddressMode; import com.zsmartsystems.zigbee.ZigBeeProfileType; @@ -733,11 +734,43 @@ public IeeeAddress getIeeeAddress() { return ieeeAddress; } + @Override + public boolean setIeeeAddress(IeeeAddress ieeeAddress) { + EmberNcp ncp = getEmberNcp(); + if (ncp.getIeeeAddress().equals(ieeeAddress)) { + // Don't write the IEEE address unless it's different since there are limitations on how many times this can + // be changed in some firmware versions. + return true; + } + return ncp.setMfgCustomEui64(ieeeAddress) == EmberStatus.EMBER_SUCCESS; + } + @Override public Integer getNwkAddress() { return nwkAddress; } + @Override + public ZigBeeStatus setNetworkState(ZigBeeNetworkState networkState) { + EmberNcp ncp = getEmberNcp(); + switch (networkState) { + case UNINITIALISED: + // Reset the NCP to "factory default" + // Note that tokenFactoryReset was introduced in firmware 7.3 (approx) and older versions used the same + // ID for another function. + // We don't check the result here for that reason. + // Note that the impact of this function not working is that the IEEE address can only be written to the + // token area once - subsequent writes will fail, and therefore changing IEEE address (eg from a + // backup/restore) may fail. + ncp.tokenFactoryReset(false, false); + return ncp.leaveNetwork() == EmberStatus.EMBER_SUCCESS ? ZigBeeStatus.SUCCESS : ZigBeeStatus.FAILURE; + case ONLINE: + return ncp.networkInit() == EmberStatus.EMBER_SUCCESS ? ZigBeeStatus.SUCCESS : ZigBeeStatus.FAILURE; + default: + return ZigBeeStatus.INVALID_ARGUMENTS; + } + } + @Override public void sendCommand(final int msgTag, final ZigBeeApsFrame apsFrame) { if (!isConfigured) { diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/EzspFrame.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/EzspFrame.java index 73ec2489f..a5f9edf3b 100644 --- a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/EzspFrame.java +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/EzspFrame.java @@ -95,7 +95,6 @@ public abstract class EzspFrame { protected static final int FRAME_ID_ADD_TRANSIENT_LINK_KEY = 0xAF; protected static final int FRAME_ID_ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x5B; protected static final int FRAME_ID_AES_MMO_HASH = 0x6F; - protected static final int FRAME_ID_BECOME_TRUST_CENTER = 0x77; protected static final int FRAME_ID_BINDING_IS_ACTIVE = 0x2E; protected static final int FRAME_ID_CALCULATE_SMACS = 0x9F; protected static final int FRAME_ID_CALCULATE_SMACS283K1 = 0xEA; @@ -213,6 +212,7 @@ public abstract class EzspFrame { protected static final int FRAME_ID_REMOTE_SET_BINDING_HANDLER = 0x31; protected static final int FRAME_ID_REMOVE_DEVICE = 0xA8; protected static final int FRAME_ID_REQUEST_LINK_KEY = 0x14; + protected static final int FRAME_ID_RESET_NODE = 0x104; protected static final int FRAME_ID_RESET_TO_FACTORY_DEFAULTS = 0xCC; protected static final int FRAME_ID_SCAN_COMPLETE_HANDLER = 0x1C; protected static final int FRAME_ID_SEND_BROADCAST = 0x36; @@ -248,6 +248,7 @@ public abstract class EzspFrame { protected static final int FRAME_ID_START_SCAN = 0x1A; protected static final int FRAME_ID_STOP_SCAN = 0x1D; protected static final int FRAME_ID_SWITCH_NETWORK_KEY_HANDLER = 0x6E; + protected static final int FRAME_ID_TOKEN_FACTORY_RESET = 0x77; protected static final int FRAME_ID_TRUST_CENTER_JOIN_HANDLER = 0x24; protected static final int FRAME_ID_VERSION = 0x00; protected static final int FRAME_ID_ZIGBEE_KEY_ESTABLISHMENT_HANDLER = 0x9B; @@ -265,7 +266,6 @@ public abstract class EzspFrame { ezspHandlerMap.put(FRAME_ID_ADD_TRANSIENT_LINK_KEY, EzspAddTransientLinkKeyResponse.class); ezspHandlerMap.put(FRAME_ID_ADDRESS_TABLE_ENTRY_IS_ACTIVE, EzspAddressTableEntryIsActiveResponse.class); ezspHandlerMap.put(FRAME_ID_AES_MMO_HASH, EzspAesMmoHashResponse.class); - ezspHandlerMap.put(FRAME_ID_BECOME_TRUST_CENTER, EzspBecomeTrustCenterResponse.class); ezspHandlerMap.put(FRAME_ID_BINDING_IS_ACTIVE, EzspBindingIsActiveResponse.class); ezspHandlerMap.put(FRAME_ID_CALCULATE_SMACS, EzspCalculateSmacsResponse.class); ezspHandlerMap.put(FRAME_ID_CALCULATE_SMACS283K1, EzspCalculateSmacs283k1Response.class); @@ -383,6 +383,7 @@ public abstract class EzspFrame { ezspHandlerMap.put(FRAME_ID_REMOTE_SET_BINDING_HANDLER, EzspRemoteSetBindingHandler.class); ezspHandlerMap.put(FRAME_ID_REMOVE_DEVICE, EzspRemoveDeviceResponse.class); ezspHandlerMap.put(FRAME_ID_REQUEST_LINK_KEY, EzspRequestLinkKeyResponse.class); + ezspHandlerMap.put(FRAME_ID_RESET_NODE, EzspResetNodeResponse.class); ezspHandlerMap.put(FRAME_ID_RESET_TO_FACTORY_DEFAULTS, EzspResetToFactoryDefaultsResponse.class); ezspHandlerMap.put(FRAME_ID_SCAN_COMPLETE_HANDLER, EzspScanCompleteHandler.class); ezspHandlerMap.put(FRAME_ID_SEND_BROADCAST, EzspSendBroadcastResponse.class); @@ -418,6 +419,7 @@ public abstract class EzspFrame { ezspHandlerMap.put(FRAME_ID_START_SCAN, EzspStartScanResponse.class); ezspHandlerMap.put(FRAME_ID_STOP_SCAN, EzspStopScanResponse.class); ezspHandlerMap.put(FRAME_ID_SWITCH_NETWORK_KEY_HANDLER, EzspSwitchNetworkKeyHandler.class); + ezspHandlerMap.put(FRAME_ID_TOKEN_FACTORY_RESET, EzspTokenFactoryResetResponse.class); ezspHandlerMap.put(FRAME_ID_TRUST_CENTER_JOIN_HANDLER, EzspTrustCenterJoinHandler.class); ezspHandlerMap.put(FRAME_ID_VERSION, EzspVersionResponse.class); ezspHandlerMap.put(FRAME_ID_ZIGBEE_KEY_ESTABLISHMENT_HANDLER, EzspZigbeeKeyEstablishmentHandler.class); diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterRequest.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterRequest.java deleted file mode 100644 index dd4f151fe..000000000 --- a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterRequest.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2016-2024 by the respective copyright holders. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; - -import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameRequest; -import com.zsmartsystems.zigbee.dongle.ember.ezsp.structure.EmberKeyData; -import com.zsmartsystems.zigbee.dongle.ember.internal.serializer.EzspSerializer; - -/** - * Class to implement the Ember EZSP command becomeTrustCenter. - *

- * This function causes a coordinator to become the Trust Center when it is operating in a - * network that is not using one. It will send out an updated Network Key to all devices that will - * indicate a transition of the network to now use a Trust Center. The Trust Center should also - * switch all devices to using this new network key with the appropriate API. - *

- * This class provides methods for processing EZSP commands. - *

- * Note that this code is autogenerated. Manual changes may be overwritten. - * - * @author Chris Jackson - Initial contribution of Java code generator - */ -public class EzspBecomeTrustCenterRequest extends EzspFrameRequest { - public static final int FRAME_ID = 0x77; - - /** - * The key data for the Updated Network Key. - *

- * EZSP type is EmberKeyData - Java type is {@link EmberKeyData} - */ - private EmberKeyData newNetworkKey; - - /** - * Serialiser used to serialise to binary line data - */ - private EzspSerializer serializer; - - /** - * Request constructor - */ - public EzspBecomeTrustCenterRequest() { - frameId = FRAME_ID; - serializer = new EzspSerializer(); - } - - /** - * The key data for the Updated Network Key. - *

- * EZSP type is EmberKeyData - Java type is {@link EmberKeyData} - * - * @return the current newNetworkKey as {@link EmberKeyData} - */ - public EmberKeyData getNewNetworkKey() { - return newNetworkKey; - } - - /** - * The key data for the Updated Network Key. - * - * @param newNetworkKey the newNetworkKey to set as {@link EmberKeyData} - */ - public void setNewNetworkKey(EmberKeyData newNetworkKey) { - this.newNetworkKey = newNetworkKey; - } - - @Override - public int[] serialize() { - // Serialize the header - serializeHeader(serializer); - - // Serialize the fields - serializer.serializeEmberKeyData(newNetworkKey); - return serializer.getPayload(); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(81); - builder.append("EzspBecomeTrustCenterRequest [networkId="); - builder.append(networkId); - builder.append(", newNetworkKey="); - builder.append(newNetworkKey); - builder.append(']'); - return builder.toString(); - } -} diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterResponse.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterResponse.java deleted file mode 100644 index 51638321a..000000000 --- a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspBecomeTrustCenterResponse.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2016-2024 by the respective copyright holders. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; - -import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameResponse; -import com.zsmartsystems.zigbee.dongle.ember.ezsp.structure.EmberStatus; - -/** - * Class to implement the Ember EZSP command becomeTrustCenter. - *

- * This function causes a coordinator to become the Trust Center when it is operating in a - * network that is not using one. It will send out an updated Network Key to all devices that will - * indicate a transition of the network to now use a Trust Center. The Trust Center should also - * switch all devices to using this new network key with the appropriate API. - *

- * This class provides methods for processing EZSP commands. - *

- * Note that this code is autogenerated. Manual changes may be overwritten. - * - * @author Chris Jackson - Initial contribution of Java code generator - */ -public class EzspBecomeTrustCenterResponse extends EzspFrameResponse { - public static final int FRAME_ID = 0x77; - - /** - * The response status. - *

- * EZSP type is EmberStatus - Java type is {@link EmberStatus} - */ - private EmberStatus status; - - /** - * Response and Handler constructor - */ - public EzspBecomeTrustCenterResponse(int[] inputBuffer) { - // Super creates deserializer and reads header fields - super(inputBuffer); - - // Deserialize the fields - status = deserializer.deserializeEmberStatus(); - } - - /** - * The response status. - *

- * EZSP type is EmberStatus - Java type is {@link EmberStatus} - * - * @return the current status as {@link EmberStatus} - */ - public EmberStatus getStatus() { - return status; - } - - /** - * The response status. - * - * @param status the status to set as {@link EmberStatus} - */ - public void setStatus(EmberStatus status) { - this.status = status; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(82); - builder.append("EzspBecomeTrustCenterResponse [networkId="); - builder.append(networkId); - builder.append(", status="); - builder.append(status); - builder.append(']'); - return builder.toString(); - } -} diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeRequest.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeRequest.java new file mode 100644 index 000000000..b3bcee96b --- /dev/null +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeRequest.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; + +import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameRequest; +import com.zsmartsystems.zigbee.dongle.ember.internal.serializer.EzspSerializer; + +/** + * Class to implement the Ember EZSP command resetNode. + *

+ * Reset the node by calling halReboot. + *

+ * This class provides methods for processing EZSP commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class EzspResetNodeRequest extends EzspFrameRequest { + public static final int FRAME_ID = 0x104; + + /** + * Serialiser used to serialise to binary line data + */ + private EzspSerializer serializer; + + /** + * Request constructor + */ + public EzspResetNodeRequest() { + frameId = FRAME_ID; + serializer = new EzspSerializer(); + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(serializer); + + // Serialize the fields + return serializer.getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(48); + builder.append("EzspResetNodeRequest [networkId="); + builder.append(networkId); + builder.append(']'); + return builder.toString(); + } +} diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeResponse.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeResponse.java new file mode 100644 index 000000000..c94a34d94 --- /dev/null +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspResetNodeResponse.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; + +import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameResponse; + +/** + * Class to implement the Ember EZSP command resetNode. + *

+ * Reset the node by calling halReboot. + *

+ * This class provides methods for processing EZSP commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class EzspResetNodeResponse extends EzspFrameResponse { + public static final int FRAME_ID = 0x104; + + /** + * Response and Handler constructor + */ + public EzspResetNodeResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + // Deserialize the fields + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(49); + builder.append("EzspResetNodeResponse [networkId="); + builder.append(networkId); + builder.append(']'); + return builder.toString(); + } +} diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetRequest.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetRequest.java new file mode 100644 index 000000000..ac0f4c058 --- /dev/null +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetRequest.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; + +import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameRequest; +import com.zsmartsystems.zigbee.dongle.ember.internal.serializer.EzspSerializer; + +/** + * Class to implement the Ember EZSP command tokenFactoryReset. + *

+ * Factory reset all configured Zigbee tokens. + *

+ * This class provides methods for processing EZSP commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class EzspTokenFactoryResetRequest extends EzspFrameRequest { + public static final int FRAME_ID = 0x77; + + /** + * Exclude network and APS outgoing frame counter tokens. + *

+ * EZSP type is bool - Java type is {@link boolean} + */ + private boolean excludeOutgoingFC; + + /** + * Exclude stack boot counter token. + *

+ * EZSP type is bool - Java type is {@link boolean} + */ + private boolean excludeBootCounter; + + /** + * Serialiser used to serialise to binary line data + */ + private EzspSerializer serializer; + + /** + * Request constructor + */ + public EzspTokenFactoryResetRequest() { + frameId = FRAME_ID; + serializer = new EzspSerializer(); + } + + /** + * Exclude network and APS outgoing frame counter tokens. + *

+ * EZSP type is bool - Java type is {@link boolean} + * + * @return the current excludeOutgoingFC as {@link boolean} + */ + public boolean getExcludeOutgoingFC() { + return excludeOutgoingFC; + } + + /** + * Exclude network and APS outgoing frame counter tokens. + * + * @param excludeOutgoingFC the excludeOutgoingFC to set as {@link boolean} + */ + public void setExcludeOutgoingFC(boolean excludeOutgoingFC) { + this.excludeOutgoingFC = excludeOutgoingFC; + } + + /** + * Exclude stack boot counter token. + *

+ * EZSP type is bool - Java type is {@link boolean} + * + * @return the current excludeBootCounter as {@link boolean} + */ + public boolean getExcludeBootCounter() { + return excludeBootCounter; + } + + /** + * Exclude stack boot counter token. + * + * @param excludeBootCounter the excludeBootCounter to set as {@link boolean} + */ + public void setExcludeBootCounter(boolean excludeBootCounter) { + this.excludeBootCounter = excludeBootCounter; + } + + @Override + public int[] serialize() { + // Serialize the header + serializeHeader(serializer); + + // Serialize the fields + serializer.serializeBool(excludeOutgoingFC); + serializer.serializeBool(excludeBootCounter); + return serializer.getPayload(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(106); + builder.append("EzspTokenFactoryResetRequest [networkId="); + builder.append(networkId); + builder.append(", excludeOutgoingFC="); + builder.append(excludeOutgoingFC); + builder.append(", excludeBootCounter="); + builder.append(excludeBootCounter); + builder.append(']'); + return builder.toString(); + } +} diff --git a/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetResponse.java b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetResponse.java new file mode 100644 index 000000000..72f3f46b9 --- /dev/null +++ b/com.zsmartsystems.zigbee.dongle.ember/src/main/java/com/zsmartsystems/zigbee/dongle/ember/ezsp/command/EzspTokenFactoryResetResponse.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee.dongle.ember.ezsp.command; + +import com.zsmartsystems.zigbee.dongle.ember.ezsp.EzspFrameResponse; + +/** + * Class to implement the Ember EZSP command tokenFactoryReset. + *

+ * Factory reset all configured Zigbee tokens. + *

+ * This class provides methods for processing EZSP commands. + *

+ * Note that this code is autogenerated. Manual changes may be overwritten. + * + * @author Chris Jackson - Initial contribution of Java code generator + */ +public class EzspTokenFactoryResetResponse extends EzspFrameResponse { + public static final int FRAME_ID = 0x77; + + /** + * Response and Handler constructor + */ + public EzspTokenFactoryResetResponse(int[] inputBuffer) { + // Super creates deserializer and reads header fields + super(inputBuffer); + + // Deserialize the fields + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(57); + builder.append("EzspTokenFactoryResetResponse [networkId="); + builder.append(networkId); + builder.append(']'); + return builder.toString(); + } +} diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeBackupManager.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeBackupManager.java new file mode 100644 index 000000000..58a5da62e --- /dev/null +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeBackupManager.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import com.zsmartsystems.zigbee.database.ZigBeeNetworkBackupDao; +import com.zsmartsystems.zigbee.database.ZigBeeNetworkDataStore; +import com.zsmartsystems.zigbee.database.ZigBeeNetworkDatabaseManager; +import com.zsmartsystems.zigbee.database.ZigBeeNodeDao; + +/** + * @author Chris Jackson + */ +public class ZigBeeBackupManager { + private final ZigBeeNetworkManager networkManager; + private final ZigBeeNetworkDatabaseManager databaseManager; + + /** + * Instantiates the {@link ZigBeeBackupManager} class + * + * @param networkManager + * @param databaseManager + */ + public ZigBeeBackupManager(ZigBeeNetworkManager networkManager, ZigBeeNetworkDatabaseManager databaseManager) { + this.networkManager = networkManager; + this.databaseManager = databaseManager; + } + + /** + * Creates a backup of the {@link ZigBeeNetworkManager}, storing it in the {@link ZigBeeNetworkDataStore} + * + * @return a unique {@link UUID} referencing the backup + */ + public UUID createBackup() { + ZigBeeNetworkBackupDao backup = new ZigBeeNetworkBackupDao(); + + backup.setUuid(UUID.randomUUID()); + backup.setDate(new Date()); + + backup.setPan(networkManager.getZigBeePanId()); + backup.setEpan(networkManager.getZigBeeExtendedPanId()); + backup.setChannel(networkManager.getZigBeeChannel()); + backup.setNetworkKey(networkManager.getZigBeeNetworkKey()); + backup.setLinkKey(networkManager.getZigBeeLinkKey()); + + Set nodesDao = new HashSet<>(); + for (ZigBeeNode node : networkManager.getNodes()) { + nodesDao.add(node.getDao()); + } + backup.setNodes(nodesDao); + + return databaseManager.writeBackup(backup) ? backup.getUuid() : null; + } + + /** + * Restores the backup referenced from the provided {@link UUID}. + * + * @param uuid the unique {@link UUID} referencing the backup to restore + * @return + */ + public boolean restoreBackup(UUID uuid) { + return false; + } + +} diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeNetworkManager.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeNetworkManager.java index e6f90e251..050ed03b0 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeNetworkManager.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/ZigBeeNetworkManager.java @@ -13,11 +13,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -36,8 +38,10 @@ import com.zsmartsystems.zigbee.app.discovery.ZigBeeDiscoveryExtension; import com.zsmartsystems.zigbee.aps.ApsDataEntity; import com.zsmartsystems.zigbee.aps.ZigBeeApsFrame; +import com.zsmartsystems.zigbee.database.ZigBeeNetworkBackupDao; import com.zsmartsystems.zigbee.database.ZigBeeNetworkDataStore; import com.zsmartsystems.zigbee.database.ZigBeeNetworkDatabaseManager; +import com.zsmartsystems.zigbee.database.ZigBeeNodeDao; import com.zsmartsystems.zigbee.groups.ZigBeeGroup; import com.zsmartsystems.zigbee.groups.ZigBeeNetworkGroupManager; import com.zsmartsystems.zigbee.groups.ZigBeeNetworkGroupManager.GroupSynchronizationMethod; @@ -1833,14 +1837,12 @@ public void run() { return true; } else { logger.trace("{}: Refresh Node notifyListener LATCH Timeout, remaining = {}", - currentNode.getIeeeAddress(), - latch.getCount()); + currentNode.getIeeeAddress(), latch.getCount()); return false; } } catch (InterruptedException e) { logger.trace("{}: Refresh Node notifyListener LATCH Interrupted, remaining = {}", - currentNode.getIeeeAddress(), - latch.getCount()); + currentNode.getIeeeAddress(), latch.getCount()); return false; } }); @@ -2048,4 +2050,134 @@ public void receiveCommandState(int msgTag, ZigBeeTransportProgressState state) transactionManager.receiveCommandState(msgTag, state); } } + + /** + * Creates a backup of the {@link ZigBeeNetworkManager}, storing it in the {@link ZigBeeNetworkDataStore} + * + * @return a unique {@link UUID} referencing the backup + */ + public UUID createBackup() { + ZigBeeNetworkBackupDao backup = new ZigBeeNetworkBackupDao(); + + backup.setUuid(UUID.randomUUID()); + backup.setDate(new Date()); + + backup.setPan(getZigBeePanId()); + backup.setEpan(getZigBeeExtendedPanId()); + backup.setChannel(getZigBeeChannel()); + backup.setNetworkKey(getZigBeeNetworkKey()); + backup.setLinkKey(getZigBeeLinkKey()); + + Set nodesDao = new HashSet<>(); + for (ZigBeeNode node : getNodes()) { + nodesDao.add(node.getDao()); + } + backup.setNodes(nodesDao); + + return databaseManager.writeBackup(backup) ? backup.getUuid() : null; + } + + /** + * Restores the backup referenced from the provided {@link UUID}. + * + * @param uuid the unique {@link UUID} referencing the backup to restore + * @return ZigBeeStatus.SUCCESS if the backup was restored + */ + public ZigBeeStatus restoreBackup(UUID uuid) { + ZigBeeNetworkBackupDao backup = databaseManager.readBackup(uuid); + if (backup == null) { + logger.debug("RestoreBackup: Failed to read {}", uuid); + return ZigBeeStatus.INVALID_ARGUMENTS; + } + logger.debug("RestoreBackup: Backup read from {}", uuid); + + // Take the network offline for reconfiguration + transport.setNetworkState(ZigBeeNetworkState.UNINITIALISED); + + // To properly re-add nodes, we must be INITIALIZING + // To call startup, we must be INITIALIZING + // Setting the state to INITIALIZING should also stop listeners receiving + // notifications as the nodes are removed and added. + setNetworkState(ZigBeeNetworkState.INITIALISING); + + // Find the coordinator + ZigBeeNodeDao coordinator = null; + for (ZigBeeNodeDao node : backup.getNodes()) { + if (node.getNetworkAddress() == 0) { + coordinator = node; + break; + } + } + + logger.debug("RestoreBackup: Coordinator {}found {}", coordinator == null ? "not " : "", + coordinator == null ? "" : coordinator.getIeeeAddress()); + + // Set the coordinator address + if (coordinator != null) { + transport.setIeeeAddress(coordinator.getIeeeAddress()); + transport.setNwkAddress(coordinator.getNetworkAddress()); + } + + long secondsSince = new Date().getTime() - backup.getDate().getTime(); + ZigBeeKey key = backup.getNetworkKey(); + + // Frame counters need to be incremented + if (key.hasIncomingFrameCounter()) { + key.setIncomingFrameCounter((int) (key.getIncomingFrameCounter() + secondsSince * 5)); + } + if (key.hasOutgoingFrameCounter()) { + key.setOutgoingFrameCounter((int) (key.getOutgoingFrameCounter() + secondsSince * 5)); + } + + // Set the network configuration + if (setZigBeePanId(backup.getPan()) != ZigBeeStatus.SUCCESS + || setZigBeeExtendedPanId(backup.getEpan()) != ZigBeeStatus.SUCCESS + || setZigBeeChannel(backup.getChannel()) != ZigBeeStatus.SUCCESS + || setZigBeeNetworkKey(backup.getNetworkKey()) != ZigBeeStatus.SUCCESS + || setZigBeeLinkKey(backup.getLinkKey()) != ZigBeeStatus.SUCCESS) { + setNetworkState(ZigBeeNetworkState.OFFLINE); + return ZigBeeStatus.FAILURE; + } + + // Remove all existing nodes + for (ZigBeeNode node : networkNodes.values()) { + logger.debug("RestoreBackup: Removing node {} [{}]", node.getIeeeAddress(), node.getNetworkAddress()); + removeNode(node); + } + + // Clear the data store + databaseManager.clear(); + + // Restore + for (ZigBeeNodeDao nodeDao : backup.getNodes()) { + ZigBeeNode node = new ZigBeeNode(this, nodeDao.getIeeeAddress()); + node.setDao(nodeDao); + logger.debug("{}: RestoreBackup: Node was restored from backup.", node.getIeeeAddress()); + updateNode(node); + } + + groupManager.initialize(); + + // Start the transport layer + ZigBeeStatus status = transport.startup(true); + if (status != ZigBeeStatus.SUCCESS) { + setNetworkState(ZigBeeNetworkState.OFFLINE); + } else { + setNetworkState(ZigBeeNetworkState.ONLINE); + } + logger.debug("RestoreBackup: Completed from {} with state {}", uuid, status); + + return status; + } + + /** + * Returns a list of all backups found on the system (ie in the {@link ZigBeeDataStore}). + * The returned Set of {@link ZigBeeNetworkBackupDao} contains only the network information, and not all the + * {@link ZigBeeNode} data + * + * @return Set of {@link ZigBeeNetworkBackupDao} containing the network information + */ + public Set listBackups() { + return databaseManager.listBackups(); + } } diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkBackupDao.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkBackupDao.java new file mode 100644 index 000000000..38e449b2e --- /dev/null +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkBackupDao.java @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2016-2024 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.zsmartsystems.zigbee.database; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import com.zsmartsystems.zigbee.ExtendedPanId; +import com.zsmartsystems.zigbee.ZigBeeChannel; +import com.zsmartsystems.zigbee.security.ZigBeeKey; +import com.zsmartsystems.zigbee.transport.DeviceType; + +/** + * This class provides a clean class for serialising data relating to a system backup + * + * @author Chris Jackson + */ +public class ZigBeeNetworkBackupDao { + private UUID uuid; + private Date date; + private DeviceType deviceType; + private Integer pan; + private ExtendedPanId epan; + private ZigBeeChannel channel; + private ZigBeeKey networkKey; + private ZigBeeKey linkKey; + private Set nodes = new HashSet<>(); + + /** + * @return the uuid + */ + public UUID getUuid() { + return uuid; + } + + /** + * @param uuid the uuid to set + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + /** + * @return the date + */ + public Date getDate() { + return date; + } + + /** + * @param date the date to set + */ + public void setDate(Date date) { + this.date = date; + } + + /** + * @return the deviceType + */ + public DeviceType getDeviceType() { + return deviceType; + } + + /** + * @param deviceType the deviceType to set + */ + public void setDeviceType(DeviceType deviceType) { + this.deviceType = deviceType; + } + + /** + * @return the pan + */ + public Integer getPan() { + return pan; + } + + /** + * @param pan the pan to set + */ + public void setPan(Integer pan) { + this.pan = pan; + } + + /** + * @return the epan + */ + public ExtendedPanId getEpan() { + return epan; + } + + /** + * @param epan the epan to set + */ + public void setEpan(ExtendedPanId epan) { + this.epan = epan; + } + + /** + * @return the channel + */ + public ZigBeeChannel getChannel() { + return channel; + } + + /** + * @param channel the channel to set + */ + public void setChannel(ZigBeeChannel channel) { + this.channel = channel; + } + + /** + * @return the networkKey + */ + public ZigBeeKey getNetworkKey() { + return networkKey; + } + + /** + * @param networkKey the networkKey to set + */ + public void setNetworkKey(ZigBeeKey networkKey) { + this.networkKey = networkKey; + } + + /** + * @return the linkKey + */ + public ZigBeeKey getLinkKey() { + return linkKey; + } + + /** + * @param linkKey the linkKey to set + */ + public void setLinkKey(ZigBeeKey linkKey) { + this.linkKey = linkKey; + } + + /** + * @return the nodes + */ + public Set getNodes() { + return nodes; + } + + /** + * @param nodes the nodes to set + */ + public void setNodes(Set nodes) { + this.nodes = nodes; + } + +} diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDataStore.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDataStore.java index 4f8dba676..d4a1305a6 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDataStore.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDataStore.java @@ -7,7 +7,9 @@ */ package com.zsmartsystems.zigbee.database; +import java.util.Collections; import java.util.Set; +import java.util.UUID; import com.zsmartsystems.zigbee.IeeeAddress; @@ -23,6 +25,9 @@ *

* A simple data store is also defined to allow the system to persist system critical information between * restarts. + *

+ * Backup Read and Write methods are defined to allow the system to write and restore a single, coherent set of backup + * data for the network. * * @author Chris Jackson * @@ -82,4 +87,34 @@ default void writeObject(String key, Object object) { default Object readObject(String key) { return null; } + + /** + * Writes a {@link ZigBeeNetworkBackupDao} to the data store + * + * @param backup + * @return + */ + default boolean writeBackup(ZigBeeNetworkBackupDao backup) { + return false; + } + + /** + * Reads a {@link ZigBeeNetworkBackupDao} from the data store + * + * @param uuid the {@link UUID} of the backup + * @return the restored {@link ZigBeeNetworkBackupDao} or null + */ + default ZigBeeNetworkBackupDao readBackup(UUID uuid) { + return null; + } + + /** + * Returns a summary of all backups in the system. + * + * @return A {@link Set} of {@link ZigBeeNetworkBackupDao} containing at least the network information + */ + default Set listBackups() { + return Collections.emptySet(); + } + } diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDatabaseManager.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDatabaseManager.java index 323867303..6e93bd244 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDatabaseManager.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/database/ZigBeeNetworkDatabaseManager.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -308,4 +309,16 @@ public void writeKey(String key, Object value) { dataStore.writeObject(key, value); } + public boolean writeBackup(ZigBeeNetworkBackupDao backup) { + return dataStore.writeBackup(backup); + } + + public ZigBeeNetworkBackupDao readBackup(UUID uuid) { + return dataStore.readBackup(uuid); + } + + public Set listBackups() { + return dataStore.listBackups(); + } + } diff --git a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/transport/ZigBeeTransportTransmit.java b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/transport/ZigBeeTransportTransmit.java index 47bcad152..c3408b043 100644 --- a/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/transport/ZigBeeTransportTransmit.java +++ b/com.zsmartsystems.zigbee/src/main/java/com/zsmartsystems/zigbee/transport/ZigBeeTransportTransmit.java @@ -10,6 +10,7 @@ import com.zsmartsystems.zigbee.ExtendedPanId; import com.zsmartsystems.zigbee.IeeeAddress; import com.zsmartsystems.zigbee.ZigBeeChannel; +import com.zsmartsystems.zigbee.ZigBeeNetworkState; import com.zsmartsystems.zigbee.ZigBeeStatus; import com.zsmartsystems.zigbee.aps.ZigBeeApsFrame; import com.zsmartsystems.zigbee.security.ZigBeeKey; @@ -80,6 +81,18 @@ public interface ZigBeeTransportTransmit { */ IeeeAddress getIeeeAddress(); + /** + * Sets the {@link IeeeAddress}. Not all devices may allow the address to be set. + * + * This is used to restore the network. + * + * @param ieeeAddress the {@link IeeeAddress} to set + * @return true if the address was set + */ + default boolean setIeeeAddress(IeeeAddress ieeeAddress) { + return false; + } + /** * Returns the network address of the local device * @@ -87,6 +100,18 @@ public interface ZigBeeTransportTransmit { */ Integer getNwkAddress(); + /** + * Sets the network address. Not all devices may allow the address to be set. + * + * This is used to restore the network. + * + * @param networkAddress the address to set + * @return true if the address was set + */ + default boolean setNwkAddress(int networkAddress) { + return false; + } + /** * Sends ZigBee Cluster Library command without waiting for response. Responses are provided to the framework * through the {@link ZigBeeTransportReceive#receiveCommand(ZigBeeApsFrame)} callback. @@ -239,4 +264,15 @@ default void setDefaultLocalEndpointId(int localEndpointId) { */ default void setNodeDescriptor(IeeeAddress ieeeAddress, NodeDescriptor nodeDescriptor) { } + + /** + * Allows the network manager to set the state of the dongle. This allows the network to be taken offline for + * reconfiguration. + * + * @param networkState the {@link ZigBeeNetworkState} to set the network + * @return {@link ZigBeeStatus} with the status of function + */ + default ZigBeeStatus setNetworkState(ZigBeeNetworkState networkState) { + return ZigBeeStatus.UNSUPPORTED; + } }