Skip to content

Commit

Permalink
Update to set IEEE address in Ember NCP
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Jackson <[email protected]>
  • Loading branch information
cdjackson committed Oct 9, 2024
1 parent 68fa1b7 commit 5d07e9f
Show file tree
Hide file tree
Showing 17 changed files with 625 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -65,9 +64,9 @@ public ZigBeeDataStore(String networkId) {
if (!file.exists() && !file.mkdirs()) {
logger.error("Error creating network database folder {}", file);
}
file = new File(this.networkId + "/" + BACKUP);
file = new File(DATABASE + BACKUP);
if (!file.exists() && !file.mkdirs()) {
logger.error("Error creating network database folder {}", file);
logger.error("Error creating network backup folder {}", file);
}
}

Expand Down Expand Up @@ -151,7 +150,6 @@ public ZigBeeNodeDao readNode(IeeeAddress address) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), CHARSET))) {

Check warning

Code scanning / CodeQL

Potential input resource leak Warning

This FileInputStream is not always closed on method exit.
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 @@ -167,7 +165,6 @@ public void writeNode(ZigBeeNodeDao node) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {

Check warning

Code scanning / CodeQL

Potential output resource leak Warning

This FileOutputStream is not always closed on method exit.
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 @@ -189,7 +186,6 @@ public void writeObject(String key, Object object) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {

Check warning

Code scanning / CodeQL

Potential output resource leak Warning

This FileOutputStream is not always closed on method exit.
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 @@ -208,12 +204,12 @@ public boolean writeBackup(ZigBeeNetworkBackupDao backup) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CHARSET))) {

Check warning

Code scanning / CodeQL

Potential output resource leak Warning

This FileOutputStream is not always closed on method exit.
stream.marshal(backup, new PrettyPrintWriter(writer));
writer.close();
logger.info("ZigBee saving network backup {} complete.", backup.getUuid());
} catch (Exception e) {
logger.error("Error writing network backup: ", backup.getUuid(), e);

Check warning

Code scanning / CodeQL

Unused format argument Warning

This format call refers to 0 argument(s) but supplies 2 argument(s).
return false;
}

return false;
return true;
}

@Override
Expand All @@ -225,7 +221,6 @@ public ZigBeeNetworkBackupDao readBackup(UUID uuid) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), CHARSET))) {

Check warning

Code scanning / CodeQL

Potential input resource leak Warning

This FileInputStream is not always closed on method exit.
backup = (ZigBeeNetworkBackupDao) stream.fromXML(reader);
reader.close();
logger.info("ZigBee reading network backup {} complete.", uuid);
} catch (Exception e) {
logger.error("{}: Error reading network backup: ", uuid, e);
}
Expand All @@ -235,6 +230,33 @@ public ZigBeeNetworkBackupDao readBackup(UUID uuid) {

@Override
public Set<ZigBeeNetworkBackupDao> listBackups() {
return Collections.emptySet();
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 @@ -8,8 +8,14 @@
package com.zsmartsystems.zigbee.console;

import java.io.PrintStream;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

import com.zsmartsystems.zigbee.ZigBeeNetworkManager;
import com.zsmartsystems.zigbee.ZigBeeStatus;
import com.zsmartsystems.zigbee.database.ZigBeeNetworkBackupDao;
import com.zsmartsystems.zigbee.database.ZigBeeNodeDao;

/**
* Console command to backup the network.
Expand Down Expand Up @@ -47,28 +53,58 @@ public void process(ZigBeeNetworkManager networkManager, String[] args, PrintStr

switch (args[1].toUpperCase()) {
case "LIST":
listBackups();
listBackups(out, networkManager);
break;
case "BACKUP":
createBackup();
createBackup(out, networkManager);
break;
case "RESTORE":
restoreBackup();
restoreBackup(out, networkManager, UUID.fromString(args[2]));
break;
default:
throw new IllegalArgumentException("Unknown option '" + args[1] + "'");
}
}

private void listBackups() {
private void listBackups(PrintStream out, ZigBeeNetworkManager networkManager) {
Map<Long, ZigBeeNetworkBackupDao> 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()));
}
}

private void createBackup() {

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);
}
}

private void restoreBackup() {

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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1964,26 +1964,6 @@
</response_parameters>
</command>

<command>
<name>becomeTrustCenter</name>
<id>0x77</id>
<description>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.</description>
<command_parameters>
<parameter>
<data_type>EmberKeyData</data_type>
<name>newNetworkKey</name>
<description>The key data for the Updated Network Key.</description>
</parameter>
</command_parameters>
<response_parameters>
<parameter>
<data_type>EmberStatus</data_type>
<name>status</name>
<description>The response status.</description>
</parameter>
</response_parameters>
</command>

<command>
<name>aesMmoHash</name>
<id>0x6F</id>
Expand Down Expand Up @@ -3916,6 +3896,30 @@
<description>A command which does nothing. The Host can use this to set the sleep mode or check the status of the NCP.</description>
</command>

<command>
<name>tokenFactoryReset</name>
<id>0x0077</id>
<description>Factory reset all configured Zigbee tokens.</description>
<command_parameters>
<parameter>
<data_type>bool</data_type>
<name>excludeOutgoingFC</name>
<description>Exclude network and APS outgoing frame counter tokens.</description>
</parameter>
<parameter>
<data_type>bool</data_type>
<name>excludeBootCounter</name>
<description>Exclude stack boot counter token.</description>
</parameter>
</command_parameters>
</command>

<command>
<name>resetNode</name>
<id>0x0104</id>
<description>Reset the node by calling halReboot.</description>
</command>

<structure>
<name>EmberCertificateData</name>
<description>The implicit certificate used in CBKE.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -745,11 +746,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 5d07e9f

Please sign in to comment.