From 5d7d2a2b90d31b1e4911dd571657bf7f5ae916f5 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:46:17 +0200 Subject: [PATCH 01/17] Support EZSP version switching. --- src/adapter/ember/adapter/emberAdapter.ts | 20 +++++++++++++------- src/adapter/ember/ezsp/consts.ts | 1 + src/adapter/ember/ezsp/ezsp.ts | 5 +++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 511554561b..6e3c5355c2 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -23,6 +23,7 @@ import { EMBER_ENCRYPTION_KEY_SIZE, EUI64_SIZE, EZSP_MAX_FRAME_LENGTH, + EZSP_MIN_PROTOCOL_VERSION, EZSP_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH } from "../ezsp/consts"; @@ -1697,21 +1698,25 @@ export class EmberAdapter extends Adapter { * Does nothing if ncpNeedsResetAndInit == true. */ private async emberVersion(): Promise { - // Note that NCP == Network Co-Processor - // the EZSP protocol version that the Host is running, we are the host so we set this value - const hostEzspProtocolVer = EZSP_PROTOCOL_VERSION; // send the Host version number to the NCP. // The NCP returns the EZSP version that the NCP is running along with the stackType and stackVersion - const [ncpEzspProtocolVer, ncpStackType, ncpStackVer] = (await this.ezsp.ezspVersion(hostEzspProtocolVer)); + let [ncpEzspProtocolVer, ncpStackType, ncpStackVer] = await this.ezsp.ezspVersion(EZSP_PROTOCOL_VERSION); // verify that the stack type is what is expected if (ncpStackType !== EZSP_STACK_TYPE_MESH) { throw new Error(`Stack type ${ncpStackType} is not expected!`); } - // verify that the NCP EZSP Protocol version is what is expected - if (ncpEzspProtocolVer !== EZSP_PROTOCOL_VERSION) { - throw new Error(`NCP EZSP protocol version of ${ncpEzspProtocolVer} does not match Host version ${hostEzspProtocolVer}`); + if (ncpEzspProtocolVer === EZSP_PROTOCOL_VERSION) { + logger.debug(`NCP EZSP protocol version (${ncpEzspProtocolVer}) matches Host.`, NS); + } else if (ncpEzspProtocolVer < EZSP_PROTOCOL_VERSION && ncpEzspProtocolVer >= EZSP_MIN_PROTOCOL_VERSION) { + [ncpEzspProtocolVer, ncpStackType, ncpStackVer] = await this.ezsp.ezspVersion(ncpEzspProtocolVer); + + logger.info(`NCP EZSP protocol version (${ncpEzspProtocolVer}) lower than Host. Switched.`, NS); + } else { + throw new Error( + `NCP EZSP protocol version (${ncpEzspProtocolVer}) is not supported by Host [${EZSP_MIN_PROTOCOL_VERSION}-${EZSP_PROTOCOL_VERSION}].` + ); } logger.debug(`NCP info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`, NS); @@ -1723,6 +1728,7 @@ export class EmberAdapter extends Adapter { throw new Error(`NCP has old-style version number. Not supported.`); } + this.ezsp.setProtocolVersion(ncpEzspProtocolVer); this.version = { ezsp: ncpEzspProtocolVer, revision: `${versionStruct.major}.${versionStruct.minor}.${versionStruct.patch} [${EmberVersionType[versionStruct.type]}]`, diff --git a/src/adapter/ember/ezsp/consts.ts b/src/adapter/ember/ezsp/consts.ts index ba22cb11b0..bcf79d62c6 100644 --- a/src/adapter/ember/ezsp/consts.ts +++ b/src/adapter/ember/ezsp/consts.ts @@ -1,6 +1,7 @@ //------------------------------------------------------------------------------------------------- // EZSP Protocol +export const EZSP_MIN_PROTOCOL_VERSION = 0x0D; /** Latest EZSP protocol version */ export const EZSP_PROTOCOL_VERSION = 0x0D; diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 215583648d..4fb5d70f67 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -259,6 +259,7 @@ export enum EzspEvents { * @event 'NCP_NEEDS_RESET_AND_INIT(EzspStatus)' An error was detected that requires resetting the NCP. */ export class Ezsp extends EventEmitter { + private version: number; private readonly tickInterval: number; public readonly ash: UartAsh; private readonly buffalo: EzspBuffalo; @@ -374,6 +375,10 @@ export class Ezsp extends EventEmitter { logger.info(`======== EZSP stopped ========`, NS); } + public setProtocolVersion(version: number): void { + this.version = version; + } + /** * Check if connected. * If not, attempt to restore the connection. From 549f4f1ca16055225b0fb1cdb9207c8812e9a746 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 00:02:37 +0200 Subject: [PATCH 02/17] Add/update commands. Add v13/14 status matching. Cleanup. --- src/adapter/ember/adapter/emberAdapter.ts | 794 ++-- src/adapter/ember/adapter/oneWaitress.ts | 7 +- src/adapter/ember/adapter/requestQueue.ts | 32 +- src/adapter/ember/adapter/tokensManager.ts | 56 +- src/adapter/ember/consts.ts | 2 + src/adapter/ember/enums.ts | 1024 +++-- src/adapter/ember/ezsp/buffalo.ts | 147 +- src/adapter/ember/ezsp/enums.ts | 224 +- src/adapter/ember/ezsp/ezsp.ts | 4571 +++++++++++++------- src/adapter/ember/ezspError.ts | 10 + src/adapter/ember/types.ts | 134 +- src/adapter/ember/zdo.ts | 40 +- test/adapter/ember/requestQueue.test.ts | 127 +- 13 files changed, 4440 insertions(+), 2728 deletions(-) create mode 100644 src/adapter/ember/ezspError.ts diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 6e3c5355c2..c317c56d2c 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -21,7 +21,6 @@ import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from "../ import {Ezsp, EzspEvents} from "../ezsp/ezsp"; import { EMBER_ENCRYPTION_KEY_SIZE, - EUI64_SIZE, EZSP_MAX_FRAME_LENGTH, EZSP_MIN_PROTOCOL_VERSION, EZSP_PROTOCOL_VERSION, @@ -38,11 +37,9 @@ import {EzspBuffalo} from "../ezsp/buffalo"; import { EmberApsOption, EmberOutgoingMessageType, - EmberStatus, EzspStatus, EmberVersionType, SLStatus, - SecManFlag, EmberNodeType, EmberNetworkStatus, SecManKeyType, @@ -59,20 +56,17 @@ import { EmberDeviceUpdate, EzspNetworkScanType, EmberIncomingMessageType, + EmberTransmitPriority, } from "../enums"; import { EmberAesMmoHashContext, EmberApsFrame, - EmberEUI64, - EmberExtendedPanId, EmberInitialSecurityState, EmberKeyData, EmberMulticastId, EmberMulticastTableEntry, EmberNetworkInitStruct, EmberNetworkParameters, - EmberNodeId, - EmberPanId, EmberVersion, SecManAPSKeyMetadata, SecManContext, @@ -122,10 +116,7 @@ import { EMBER_NUM_802_15_4_CHANNELS, EMBER_MIN_802_15_4_CHANNEL_NUMBER, UNKNOWN_NETWORK_STATE, - EMBER_UNKNOWN_NODE_ID, MAXIMUM_APS_PAYLOAD_LENGTH, - APS_ENCRYPTION_OVERHEAD, - APS_FRAGMENTATION_OVERHEAD, LONG_DEST_FRAME_CONTROL, MAC_ACK_REQUIRED, MAXIMUM_INTERPAN_LENGTH, @@ -147,13 +138,15 @@ import {aesMmoHashInit, initNetworkCache, initSecurityManagerContext} from "../u import {randomBytes} from "crypto"; import {EmberOneWaitress, OneWaitressEvents} from "./oneWaitress"; import {logger} from "../../../utils/logger"; +import {EUI64, ExtendedPanId, NodeId, PanId} from '../../../zspec/tstypes'; +import {EzspError} from '../ezspError'; // import {EmberTokensManager} from "./tokensManager"; const NS = 'zh:ember'; export type NetworkCache = { //-- basic network info - eui64: EmberEUI64, + eui64: EUI64, parameters: EmberNetworkParameters, status: EmberNetworkStatus, /** uint8_t */ @@ -167,7 +160,7 @@ export type NetworkCache = { * This key may be hashed and not the actual link key currently in use. */ type LinkKeyBackupData = { - deviceEui64: EmberEUI64, + deviceEui64: EUI64, key: EmberKeyData, outgoingFrameCounter: number, incomingFrameCounter: number, @@ -328,12 +321,6 @@ const DEFAULT_STACK_CONFIG: Readonly = { * Removing `ENABLE_ROUTE_DISCOVERY` leads to devices that won't reconnect/go offline, and various other issues. Keeping it for now. */ const DEFAULT_APS_OPTIONS = (EmberApsOption.RETRY | EmberApsOption.ENABLE_ROUTE_DISCOVERY | EmberApsOption.ENABLE_ADDRESS_DISCOVERY); -/** - * Enabling this allows to immediately reject requests that won't be able to get to their destination. - * However, it causes more NCP calls, notably to get the source route overhead. - * XXX: Needs further testing before enabling - */ -const CHECK_APS_PAYLOAD_LENGTH = false; /** Time for a ZDO request to get a callback response. ASH is 2400*6 for ACK timeout. */ const DEFAULT_ZDO_REQUEST_TIMEOUT = 15000;// msec /** Time for a ZCL request to get a callback response. ASH is 2400*6 for ACK timeout. */ @@ -540,32 +527,32 @@ export class EmberAdapter extends Adapter { * Emitted from @see Ezsp.ezspStackStatusHandler * @param status */ - private async onStackStatus(status: EmberStatus): Promise { + private async onStackStatus(status: SLStatus): Promise { // to be extra careful, should clear network cache upon receiving this. this.clearNetworkCache(); switch (status) { - case EmberStatus.NETWORK_UP: { + case SLStatus.NETWORK_UP: { this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_UP); logger.info(`[STACK STATUS] Network up.`, NS); break; } - case EmberStatus.NETWORK_DOWN: { + case SLStatus.NETWORK_DOWN: { this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_DOWN); logger.info(`[STACK STATUS] Network down.`, NS); break; } - case EmberStatus.NETWORK_OPENED: { + case SLStatus.ZIGBEE_NETWORK_OPENED: { this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED); logger.info(`[STACK STATUS] Network opened.`, NS); break; } - case EmberStatus.NETWORK_CLOSED: { + case SLStatus.ZIGBEE_NETWORK_CLOSED: { this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED); logger.info(`[STACK STATUS] Network closed.`, NS); break; } - case EmberStatus.CHANNEL_CHANGED: { + case SLStatus.ZIGBEE_CHANNEL_CHANGED: { this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_CHANNEL_CHANGED); // invalidate cache this.networkCache.parameters.radioChannel = INVALID_RADIO_CHANNEL; @@ -573,7 +560,7 @@ export class EmberAdapter extends Adapter { break; } default: { - logger.debug(`[STACK STATUS] ${EmberStatus[status]}.`, NS); + logger.debug(`[STACK STATUS] ${SLStatus[status]}.`, NS); break; } } @@ -589,9 +576,9 @@ export class EmberAdapter extends Adapter { * @param messageTag * @param status */ - private async onMessageSent(type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number, - status: EmberStatus): Promise { - if (status === EmberStatus.DELIVERY_FAILED) { + private async onMessageSent(status: SLStatus, type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, + messageTag: number): Promise { + if (status === SLStatus.ZIGBEE_DELIVERY_FAILED) { // no ACK was received from the destination switch (type) { case EmberOutgoingMessageType.BROADCAST: @@ -609,7 +596,7 @@ export class EmberAdapter extends Adapter { break; } } - } else if (status === EmberStatus.SUCCESS) { + } else if (status === SLStatus.OK) { if (type === EmberOutgoingMessageType.MULTICAST && apsFrame.destinationEndpoint === 0xFF && apsFrame.groupId < EMBER_MIN_BROADCAST_ADDRESS && !this.multicastTable.includes(apsFrame.groupId)) { // workaround for devices using multicast for state update (coordinator passthrough) @@ -624,12 +611,12 @@ export class EmberAdapter extends Adapter { await new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const status = (await this.ezsp.ezspSetMulticastTableEntry(tableIdx, multicastEntry)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error( - `Failed to register group "${multicastEntry.multicastId}" in multicast table with status=${EmberStatus[status]}.`, + `Failed to register group "${multicastEntry.multicastId}" in multicast table with status=${SLStatus[status]}.`, NS ); return status; @@ -637,7 +624,7 @@ export class EmberAdapter extends Adapter { logger.debug(`Registered multicast table entry (${tableIdx}): ${JSON.stringify(multicastEntry)}.`, NS); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, (reason: Error) => { // remove to allow retry on next occurrence @@ -659,7 +646,7 @@ export class EmberAdapter extends Adapter { * @param sender The sender of the response. Should match `payload.nodeId` in many responses. * @param payload If null, the response indicated a failure. */ - private async onZDOResponse(status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: unknown) + private async onZDOResponse(status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: unknown) : Promise { this.oneWaitress.resolveZDO(status, sender, apsFrame, payload); } @@ -672,7 +659,7 @@ export class EmberAdapter extends Adapter { * @param eui64 * @param macCapFlags */ - private async onEndDeviceAnnounce(sender: EmberNodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload): Promise { + private async onEndDeviceAnnounce(sender: NodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload): Promise { // reduced function device // if ((payload.capabilities.deviceType === 0)) { @@ -690,7 +677,7 @@ export class EmberAdapter extends Adapter { * @param sender * @param messageContents */ - private async onIncomingMessage(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: EmberNodeId, + private async onIncomingMessage(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: NodeId, messageContents: Buffer): Promise { const payload: ZclPayload = { clusterID: apsFrame.clusterId, @@ -717,7 +704,7 @@ export class EmberAdapter extends Adapter { * @param lastHopLqi * @param messageContents */ - private async onTouchlinkMessage(sourcePanId: EmberPanId, sourceAddress: EmberEUI64, groupId: number | null, lastHopLqi: number, + private async onTouchlinkMessage(sourcePanId: PanId, sourceAddress: EUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer): Promise { const payload: ZclPayload = { clusterID: Zcl.Clusters.touchlink.ID, @@ -792,8 +779,8 @@ export class EmberAdapter extends Adapter { * @param policyDecision * @param parentOfNewNodeId */ - private async onTrustCenterJoin(newNodeId: EmberNodeId, newNodeEui64: EmberEUI64, status: EmberDeviceUpdate, - policyDecision: EmberJoinDecision, parentOfNewNodeId: EmberNodeId): Promise { + private async onTrustCenterJoin(newNodeId: NodeId, newNodeEui64: EUI64, status: EmberDeviceUpdate, + policyDecision: EmberJoinDecision, parentOfNewNodeId: NodeId): Promise { if (status === EmberDeviceUpdate.DEVICE_LEFT) { const payload: DeviceLeavePayload = { networkAddress: newNodeId, @@ -814,7 +801,7 @@ export class EmberAdapter extends Adapter { if (this.manufacturerCode !== joinManufCode) { await new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { logger.debug(`[WORKAROUND] Setting coordinator manufacturer code to ${Zcl.ManufacturerCode[joinManufCode]}.`, NS); await this.ezsp.ezspSetManufacturerCode(joinManufCode); @@ -822,7 +809,7 @@ export class EmberAdapter extends Adapter { this.emit(Events.deviceJoined, payload); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, true,/*prioritize*/ @@ -840,7 +827,7 @@ export class EmberAdapter extends Adapter { private async watchdogCounters(): Promise { await new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { // listed as per EmberCounterType const ncpCounters = (await this.ezsp.ezspReadAndClearCounters()); @@ -851,7 +838,7 @@ export class EmberAdapter extends Adapter { logger.info(`[ASH COUNTERS] ${ashCounters.join(',')}`, NS); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -912,8 +899,8 @@ export class EmberAdapter extends Adapter { // populate network cache info const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - if (status !== EmberStatus.SUCCESS) { - throw new Error(`Failed to get network parameters with status=${EmberStatus[status]}.`); + if (status !== SLStatus.OK) { + throw new Error(`Failed to get network parameters with status=${SLStatus[status]}.`); } this.networkCache.parameters = parameters; @@ -979,9 +966,9 @@ export class EmberAdapter extends Adapter { // After the change of ncp memory model in UC, we can not increase the default NCP table sizes anymore. // Therefore, checking for desiredTableSize == (ncp)addressTableSize might not be always true anymore // assert(desiredTableSize <= addressTableSize); - if ((status !== EzspStatus.SUCCESS) || (addressTableSize > desiredTableSize)) { + if ((status !== SLStatus.OK) || (addressTableSize > desiredTableSize)) { throw new Error( - `[INIT] NCP (${addressTableSize}) disagrees with Host (min ${desiredTableSize}) on table size. status=${EzspStatus[status]}` + `[INIT] NCP (${addressTableSize}) disagrees with Host (min ${desiredTableSize}) on table size. status=${SLStatus[status]}` ); } } @@ -1028,8 +1015,8 @@ export class EmberAdapter extends Adapter { this.stackConfig.CONCENTRATOR_MAX_HOPS, )); - if (status !== EmberStatus.SUCCESS) { - throw new Error(`[CONCENTRATOR] Failed to set concentrator with status=${status}.`); + if (status !== SLStatus.OK) { + throw new Error(`[CONCENTRATOR] Failed to set concentrator with status=${SLStatus[status]}.`); } const remainTilMTORR = (await this.ezsp.ezspSetSourceRouteDiscoveryMode(EmberSourceRouteDiscoveryMode.RESCHEDULE)); @@ -1050,7 +1037,7 @@ export class EmberAdapter extends Adapter { const [epStatus,] = (await this.ezsp.ezspGetEndpointFlags(ep.endpoint)); // endpoint not already registered - if (epStatus !== EzspStatus.SUCCESS) { + if (epStatus !== SLStatus.OK) { // check to see if ezspAddEndpoint needs to be called // if ezspInit is called without NCP reset, ezspAddEndpoint is not necessary and will return an error const status = (await this.ezsp.ezspAddEndpoint( @@ -1062,10 +1049,10 @@ export class EmberAdapter extends Adapter { ep.outClusterList.slice(),// copy )); - if (status === EzspStatus.SUCCESS) { - logger.debug(`Registered endpoint "${ep.endpoint}" with status=${EzspStatus[status]}.`, NS); + if (status === SLStatus.OK) { + logger.debug(`Registered endpoint '${ep.endpoint}'.`, NS); } else { - throw new Error(`Failed to register endpoint "${ep.endpoint}" with status=${EzspStatus[status]}.`); + throw new Error(`Failed to register endpoint "${ep.endpoint}" with status=${SLStatus[status]}.`); } } else { logger.debug(`Endpoint "${ep.endpoint}" already registered.`, NS); @@ -1080,8 +1067,8 @@ export class EmberAdapter extends Adapter { const status = (await this.ezsp.ezspSetMulticastTableEntry(this.multicastTable.length, multicastEntry)); - if (status !== EmberStatus.SUCCESS) { - throw new Error(`Failed to register group "${multicastId}" in multicast table with status=${EmberStatus[status]}.`); + if (status !== SLStatus.OK) { + throw new Error(`Failed to register group "${multicastId}" in multicast table with status=${SLStatus[status]}.`); } logger.debug(`Registered multicast table entry (${this.multicastTable.length}): ${JSON.stringify(multicastEntry)}.`, NS); @@ -1102,23 +1089,23 @@ export class EmberAdapter extends Adapter { EzspDecisionId.ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY, )); - if (status !== EzspStatus.SUCCESS) { + if (status !== SLStatus.OK) { throw new Error(`[INIT TC] Failed to set EzspPolicyId TC_KEY_REQUEST_POLICY to ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY ` - + `with status=${EzspStatus[status]}.`); + + `with status=${SLStatus[status]}.`); } const appKeyPolicy = this.stackConfig.KEY_TABLE_SIZE ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS; status = (await this.emberSetEzspPolicy(EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyPolicy)); - if (status !== EzspStatus.SUCCESS) { + if (status !== SLStatus.OK) { throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${EzspDecisionId[appKeyPolicy]} ` - + `with status=${EzspStatus[status]}.`); + + `with status=${SLStatus[status]}.`); } status = (await this.emberSetJoinPolicy(EmberJoinDecision.USE_PRECONFIGURED_KEY)); - if (status !== EzspStatus.SUCCESS) { - throw new Error(`[INIT TC] Failed to set join policy to USE_PRECONFIGURED_KEY with status=${EzspStatus[status]}.`); + if (status !== SLStatus.OK) { + throw new Error(`[INIT TC] Failed to set join policy to USE_PRECONFIGURED_KEY with status=${SLStatus[status]}.`); } } @@ -1128,15 +1115,15 @@ export class EmberAdapter extends Adapter { }; const initStatus = (await this.ezsp.ezspNetworkInit(networkInitStruct)); - logger.debug(`[INIT TC] Network init status=${EmberStatus[initStatus]}.`, NS); + logger.debug(`[INIT TC] Network init status=${SLStatus[initStatus]}.`, NS); - if ((initStatus !== EmberStatus.SUCCESS) && (initStatus !== EmberStatus.NOT_JOINED)) { - throw new Error(`[INIT TC] Failed network init request with status=${EmberStatus[initStatus]}.`); + if ((initStatus !== SLStatus.OK) && (initStatus !== SLStatus.NOT_JOINED)) { + throw new Error(`[INIT TC] Failed network init request with status=${SLStatus[initStatus]}.`); } let action: NetworkInitAction = NetworkInitAction.DONE; - if (initStatus === EmberStatus.SUCCESS) { + if (initStatus === SLStatus.OK) { // network await this.oneWaitress.startWaitingForEvent( {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_UP}, @@ -1149,13 +1136,13 @@ export class EmberAdapter extends Adapter { logger.debug(`[INIT TC] Current network config=${JSON.stringify(this.networkOptions)}`, NS); logger.debug(`[INIT TC] Current NCP network: nodeType=${EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`, NS); - if ((npStatus === EmberStatus.SUCCESS) && (nodeType === EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId) + if ((npStatus === SLStatus.OK) && (nodeType === EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId) && (equals(this.networkOptions.extendedPanID, netParams.extendedPanId))) { // config matches adapter so far, no error, we can check the network key const context = initSecurityManagerContext(); context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 0; - const [networkKey, nkStatus] = (await this.ezsp.ezspExportKey(context)); + const [nkStatus, networkKey] = (await this.ezsp.ezspExportKey(context)); if (nkStatus !== SLStatus.OK) { throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); @@ -1176,8 +1163,8 @@ export class EmberAdapter extends Adapter { logger.info(`[INIT TC] NCP network does not match config. Leaving network...`, NS); const leaveStatus = (await this.ezsp.ezspLeaveNetwork()); - if (leaveStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT TC] Failed leave network request with status=${EmberStatus[leaveStatus]}.`); + if (leaveStatus !== SLStatus.OK) { + throw new Error(`[INIT TC] Failed leave network request with status=${SLStatus[leaveStatus]}.`); } await this.oneWaitress.startWaitingForEvent( @@ -1194,7 +1181,7 @@ export class EmberAdapter extends Adapter { const backup: Backup = this.getStoredBackup(); - if ((initStatus === EmberStatus.NOT_JOINED) || (action === NetworkInitAction.LEFT)) { + if ((initStatus === SLStatus.NOT_JOINED) || (action === NetworkInitAction.LEFT)) { // no network if (backup != null) { if ((this.networkOptions.panID === backup.networkOptions.panId) @@ -1224,9 +1211,8 @@ export class EmberAdapter extends Adapter { logger.info(`[INIT TC] Forming from backup.`, NS); const keyList: LinkKeyBackupData[] = backup.devices.map((device) => { const octets = Array.from(device.ieeeAddress.reverse()); - const deviceEui64 = '0x' + octets.map(octet => octet.toString(16).padStart(2, '0')).join(""); const key: LinkKeyBackupData = { - deviceEui64, + deviceEui64: `0x${octets.map(octet => octet.toString(16).padStart(2, '0')).join('')}`, key: {contents: device.linkKey.key}, outgoingFrameCounter: device.linkKey.txCounter, incomingFrameCounter: device.linkKey.rxCounter, @@ -1285,10 +1271,10 @@ export class EmberAdapter extends Adapter { // // XXX: no idea here on the proper timer value, but this will block the network for several seconds on exec // // (probably have to take the behavior of sleepy-end devices into account to improve chances of reaching everyone right away?) // setTimeout(async () => { - // this.requestQueue.enqueue(async (): Promise => { + // this.requestQueue.enqueue(async (): Promise => { // await this.broadcastNetworkKeyUpdate(); - // return EmberStatus.SUCCESS; + // return SLStatus.OK; // }, logger.error, true);// no reject just log error if any, will retry next start, & prioritize so we know it'll run when expected // }, 300000); logger.warning(`[INIT TC] Network key frame counter is reaching its limit. A new network key will have to be instaured soon.`, NS); @@ -1300,8 +1286,8 @@ export class EmberAdapter extends Adapter { /** * Form a network using given parameters. */ - private async formNetwork(fromBackup: boolean, networkKey: Buffer, networkKeySequenceNumber: number, panId: EmberPanId, - extendedPanId: EmberExtendedPanId, radioChannel: number, tcLinkKey: Buffer): Promise { + private async formNetwork(fromBackup: boolean, networkKey: Buffer, networkKeySequenceNumber: number, panId: PanId, + extendedPanId: ExtendedPanId, radioChannel: number, tcLinkKey: Buffer): Promise { const state: EmberInitialSecurityState = { bitmask: ( EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY @@ -1318,26 +1304,26 @@ export class EmberAdapter extends Adapter { state.bitmask |= EmberInitialSecurityBitmask.NO_FRAME_COUNTER_RESET; } - let emberStatus = (await this.ezsp.ezspSetInitialSecurityState(state)); + let status = (await this.ezsp.ezspSetInitialSecurityState(state)); - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed to set initial security state with status=${EmberStatus[emberStatus]}.`); + if (status !== SLStatus.OK) { + throw new Error(`[INIT FORM] Failed to set initial security state with status=${SLStatus[status]}.`); } const extended: EmberExtendedSecurityBitmask = ( EmberExtendedSecurityBitmask.JOINER_GLOBAL_LINK_KEY | EmberExtendedSecurityBitmask.NWK_LEAVE_REQUEST_NOT_ALLOWED ); - const extSecStatus = (await this.ezsp.ezspSetExtendedSecurityBitmask(extended)); + status = (await this.ezsp.ezspSetExtendedSecurityBitmask(extended)); - if (extSecStatus !== EzspStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed to set extended security bitmask to ${extended} with status=${EzspStatus[extSecStatus]}.`); + if (status !== SLStatus.OK) { + throw new Error(`[INIT FORM] Failed to set extended security bitmask to ${extended} with status=${SLStatus[status]}.`); } if (!fromBackup && this.stackConfig.KEY_TABLE_SIZE > 0) { - emberStatus = await this.ezsp.ezspClearKeyTable(); + status = await this.ezsp.ezspClearKeyTable(); - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed to clear key table with status=${EmberStatus[emberStatus]}.`); + if (status !== SLStatus.OK) { + throw new Error(`[INIT FORM] Failed to clear key table with status=${SLStatus[status]}.`); } } @@ -1354,10 +1340,10 @@ export class EmberAdapter extends Adapter { logger.info(`[INIT FORM] Forming new network with: ${JSON.stringify(netParams)}`, NS); - emberStatus = (await this.ezsp.ezspFormNetwork(netParams)); + status = (await this.ezsp.ezspFormNetwork(netParams)); - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed form network request with status=${EmberStatus[emberStatus]}.`); + if (status !== SLStatus.OK) { + throw new Error(`[INIT FORM] Failed form network request with status=${SLStatus[status]}.`); } await this.oneWaitress.startWaitingForEvent( @@ -1366,10 +1352,9 @@ export class EmberAdapter extends Adapter { '[INIT FORM] Form network', ); - const stStatus = await this.ezsp.ezspStartWritingStackTokens(); - - logger.debug(`[INIT FORM] Start writing stack tokens status=${EzspStatus[stStatus]}.`, NS); + status = await this.ezsp.ezspStartWritingStackTokens(); + logger.debug(`[INIT FORM] Start writing stack tokens status=${SLStatus[status]}.`, NS); logger.info(`[INIT FORM] New network formed!`, NS); } @@ -1418,18 +1403,18 @@ export class EmberAdapter extends Adapter { public async exportLinkKeys(): Promise { const [confStatus, keyTableSize] = (await this.ezsp.ezspGetConfigurationValue(EzspConfigId.KEY_TABLE_SIZE)); - if (confStatus !== EzspStatus.SUCCESS) { - throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${EzspStatus[confStatus]}.`); + if (confStatus !== SLStatus.OK) { + throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${SLStatus[confStatus]}.`); } - let deviceEui64: EmberEUI64; + let context: SecManContext; let plaintextKey: SecManKey; let apsKeyMeta: SecManAPSKeyMetadata; let status: SLStatus; const keyList: LinkKeyBackupData[] = []; for (let i = 0; i < keyTableSize; i++) { - [deviceEui64, plaintextKey, apsKeyMeta, status] = (await this.ezsp.ezspExportLinkKeyByIndex(i)); + [status, context, plaintextKey, apsKeyMeta] = (await this.ezsp.ezspExportLinkKeyByIndex(i)); logger.debug(`[BACKUP] Export link key at index ${i}, status=${SLStatus[status]}.`, NS); // only include key if we could retrieve one at index and hash it properly @@ -1439,16 +1424,16 @@ export class EmberAdapter extends Adapter { // This is per the Smart Energy spec. const [hashStatus, hashedKey] = (await this.emberAesHashSimple(plaintextKey.contents)); - if (hashStatus === EmberStatus.SUCCESS) { + if (hashStatus === SLStatus.OK) { keyList.push({ - deviceEui64, + deviceEui64: context.eui64, key: {contents: hashedKey}, outgoingFrameCounter: apsKeyMeta.outgoingFrameCounter, incomingFrameCounter: apsKeyMeta.incomingFrameCounter, }); } else { // this should never happen? - logger.error(`[BACKUP] Failed to hash link key at index ${i} with status=${EmberStatus[hashStatus]}. Omitting from backup.`, NS); + logger.error(`[BACKUP] Failed to hash link key at index ${i} with status=${SLStatus[hashStatus]}. Omitting from backup.`, NS); } } } @@ -1470,8 +1455,8 @@ export class EmberAdapter extends Adapter { const [confStatus, keyTableSize] = (await this.ezsp.ezspGetConfigurationValue(EzspConfigId.KEY_TABLE_SIZE)); - if (confStatus !== EzspStatus.SUCCESS) { - throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${EzspStatus[confStatus]}.`); + if (confStatus !== SLStatus.OK) { + throw new Error(`[BACKUP] Failed to retrieve key table size from NCP with status=${SLStatus[confStatus]}.`); } if (backupData.length > keyTableSize) { @@ -1484,20 +1469,19 @@ export class EmberAdapter extends Adapter { throw new Error(`[BACKUP] Cannot import TC data while network is up, networkStatus=${EmberNetworkStatus[networkStatus]}.`); } - let status: EmberStatus; + let status: SLStatus; for (let i = 0; i < keyTableSize; i++) { if (i >= backupData.length) { // erase any key index not present in backup but available on the NCP status = (await this.ezsp.ezspEraseKeyTableEntry(i)); } else { - const importStatus = (await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key)); - status = ((importStatus === SLStatus.OK) ? EmberStatus.SUCCESS : EmberStatus.KEY_TABLE_INVALID_ADDRESS); + status = (await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key)); } - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { throw new Error(`[BACKUP] Failed to ${((i >= backupData.length) ? "erase" : "set")} key table entry at index ${i} ` - + `with status=${EmberStatus[status]}`); + + `with status=${SLStatus[status]}`); } } @@ -1513,14 +1497,14 @@ export class EmberAdapter extends Adapter { private async broadcastNetworkKeyUpdate(): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { logger.warning(`[TRUST CENTER] Performing a network key update. This might take a while and disrupt normal operation.`, NS); // zero-filled = let stack generate new random network key let status = await this.ezsp.ezspBroadcastNextNetworkKey({contents: Buffer.alloc(EMBER_ENCRYPTION_KEY_SIZE)}); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[TRUST CENTER] Failed to broadcast next network key with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[TRUST CENTER] Failed to broadcast next network key with status=${SLStatus[status]}.`, NS); return status; } @@ -1530,9 +1514,9 @@ export class EmberAdapter extends Adapter { status = (await this.ezsp.ezspBroadcastNetworkKeySwitch()); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { // XXX: Not sure how likely this is, but this is bad, probably should hard fail? - logger.error(`[TRUST CENTER] Failed to broadcast network key switch with status=${EmberStatus[status]}.`, NS); + logger.error(`[TRUST CENTER] Failed to broadcast network key switch with status=${SLStatus[status]}.`, NS); return status; } @@ -1594,7 +1578,7 @@ export class EmberAdapter extends Adapter { * This call caches the results on the host to prevent frequent EZSP transactions. * Check against BLANK_EUI64 for validity. */ - public async emberGetEui64(): Promise { + public async emberGetEui64(): Promise { if (this.networkCache.eui64 === ZSpec.BLANK_EUI64) { this.networkCache.eui64 = (await this.ezsp.ezspGetEui64()); } @@ -1607,14 +1591,14 @@ export class EmberAdapter extends Adapter { * This call caches the results on the host to prevent frequent EZSP transactions. * Check against INVALID_PAN_ID for validity. */ - public async emberGetPanId(): Promise { + public async emberGetPanId(): Promise { if (this.networkCache.parameters.panId === ZSpec.INVALID_PAN_ID) { const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - if (status === EmberStatus.SUCCESS) { + if (status === SLStatus.OK) { this.networkCache.parameters = parameters; } else { - logger.error(`Failed to get PAN ID (via network parameters) with status=${EmberStatus[status]}.`, NS); + logger.error(`Failed to get PAN ID (via network parameters) with status=${SLStatus[status]}.`, NS); } } @@ -1626,14 +1610,14 @@ export class EmberAdapter extends Adapter { * This call caches the results on the host to prevent frequent EZSP transactions. * Check against BLANK_EXTENDED_PAN_ID for validity. */ - public async emberGetExtendedPanId(): Promise { + public async emberGetExtendedPanId(): Promise { if (equals(this.networkCache.parameters.extendedPanId, ZSpec.BLANK_EXTENDED_PAN_ID)) { const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - if (status === EmberStatus.SUCCESS) { + if (status === SLStatus.OK) { this.networkCache.parameters = parameters; } else { - logger.error(`Failed to get Extended PAN ID (via network parameters) with status=${EmberStatus[status]}.`, NS); + logger.error(`Failed to get Extended PAN ID (via network parameters) with status=${SLStatus[status]}.`, NS); } } @@ -1649,10 +1633,10 @@ export class EmberAdapter extends Adapter { if (this.networkCache.parameters.radioChannel === INVALID_RADIO_CHANNEL) { const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - if (status === EmberStatus.SUCCESS) { + if (status === SLStatus.OK) { this.networkCache.parameters = parameters; } else { - logger.error(`Failed to get radio channel (via network parameters) with status=${EmberStatus[status]}.`, NS); + logger.error(`Failed to get radio channel (via network parameters) with status=${SLStatus[status]}.`, NS); } } @@ -1663,7 +1647,7 @@ export class EmberAdapter extends Adapter { public async emberStartEnergyScan(): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const status = (await this.ezsp.ezspStartScan( EzspNetworkScanType.ENERGY_SCAN, EMBER_ALL_802_15_4_CHANNELS_MASK, @@ -1672,13 +1656,13 @@ export class EmberAdapter extends Adapter { if (status !== SLStatus.OK) { logger.error(`Failed energy scan request with status=${SLStatus[status]}.`, NS); - return EmberStatus.ERR_FATAL; + return SLStatus.FAIL; } // TODO: result in logs only atm, since UI doesn't support it resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -1719,16 +1703,16 @@ export class EmberAdapter extends Adapter { ); } + this.ezsp.setProtocolVersion(ncpEzspProtocolVer); logger.debug(`NCP info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`, NS); const [status, versionStruct] = (await this.ezsp.ezspGetVersionStruct()); - if (status !== EzspStatus.SUCCESS) { + if (status !== SLStatus.OK) { // Should never happen with support of only EZSP v13+ throw new Error(`NCP has old-style version number. Not supported.`); } - this.ezsp.setProtocolVersion(ncpEzspProtocolVer); this.version = { ezsp: ncpEzspProtocolVer, revision: `${versionStruct.major}.${versionStruct.minor}.${versionStruct.patch} [${EmberVersionType[versionStruct.type]}]`, @@ -1750,17 +1734,14 @@ export class EmberAdapter extends Adapter { * @param value uint16_t * @returns */ - private async emberSetEzspConfigValue(configId: EzspConfigId, value: number): Promise { + private async emberSetEzspConfigValue(configId: EzspConfigId, value: number): Promise { const status = (await this.ezsp.ezspSetConfigurationValue(configId, value)); - logger.debug(`[EzspConfigId] SET "${EzspConfigId[configId]}" TO "${value}" with status=${EzspStatus[status]}.`, NS); + logger.debug(`[EzspConfigId] SET "${EzspConfigId[configId]}" TO "${value}" with status=${SLStatus[status]}.`, NS); - if (status === EzspStatus.ERROR_INVALID_ID) { - // can be ZLL where not all NCPs need or support it. - logger.warning(`[EzspConfigId] Unsupported configuration ID ${EzspConfigId[configId]} by NCP.`, NS); - } else if (status !== EzspStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.warning( - `[EzspConfigId] Failed to SET "${EzspConfigId[configId]}" TO "${value}" with status=${EzspStatus[status]}. ` + `[EzspConfigId] Failed to SET "${EzspConfigId[configId]}" TO "${value}" with status=${SLStatus[status]}. ` + `Firmware value will be used instead.`, NS, ); @@ -1776,10 +1757,10 @@ export class EmberAdapter extends Adapter { * @param value uint8_t * * @returns */ - private async emberSetEzspValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { + private async emberSetEzspValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { const status = (await this.ezsp.ezspSetValue(valueId, valueLength, value)); - logger.debug(`[EzspValueId] SET "${EzspValueId[valueId]}" TO "${value}" with status=${EzspStatus[status]}.`, NS); + logger.debug(`[EzspValueId] SET "${EzspValueId[valueId]}" TO "${value}" with status=${SLStatus[status]}.`, NS); return status; } @@ -1790,10 +1771,10 @@ export class EmberAdapter extends Adapter { * @param decisionId Can be bitop * @returns */ - private async emberSetEzspPolicy(policyId: EzspPolicyId, decisionId: number): Promise { + private async emberSetEzspPolicy(policyId: EzspPolicyId, decisionId: number): Promise { const status = (await this.ezsp.ezspSetPolicy(policyId, decisionId)); - logger.debug(`[EzspPolicyId] SET "${EzspPolicyId[policyId]}" TO "${decisionId}" with status=${EzspStatus[status]}.`, NS); + logger.debug(`[EzspPolicyId] SET "${EzspPolicyId[policyId]}" TO "${decisionId}" with status=${SLStatus[status]}.`, NS); return status; } @@ -1810,9 +1791,10 @@ export class EmberAdapter extends Adapter { * @returns result context or null */ private async aesMmoHash(context: EmberAesMmoHashContext, finalize: boolean, data: Buffer): - Promise<[EmberStatus, reContext: EmberAesMmoHashContext]> { + Promise<[SLStatus, reContext: EmberAesMmoHashContext]> { if (data.length > 255) { - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); + // will be caught by request queue and rejected internally. + throw new EzspError(EzspStatus.ERROR_INVALID_CALL); } const [status, reContext] = (await this.ezsp.ezspAesMmoHash(context, finalize, data)); @@ -1828,13 +1810,13 @@ export class EmberAdapter extends Adapter { * @param context EmberAesMmoHashContext* A pointer to the location of the hash context to update. * @param data const uint8_t* A pointer to the location of the data to hash. * - * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was + * @returns An ::SLStatus value indicating EMBER_SUCCESS if the hash was * calculated successfully. EMBER_INVALID_CALL if the block size is not a * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the * data exceeds the maximum limits of the hash function. * @returns result context or null */ - private async emberAesMmoHashUpdate(context: EmberAesMmoHashContext, data: Buffer): Promise<[EmberStatus, reContext: EmberAesMmoHashContext]> { + private async emberAesMmoHashUpdate(context: EmberAesMmoHashContext, data: Buffer): Promise<[SLStatus, reContext: EmberAesMmoHashContext]> { return this.aesMmoHash(context, false/*finalize?*/, data); } @@ -1849,13 +1831,13 @@ export class EmberAdapter extends Adapter { * @param context EmberAesMmoHashContext * A pointer to the location of the hash context to finalize. * @param data uint8_t * A pointer to the location of data to hash. May be NULL. * - * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was + * @returns An ::SLStatus value indicating EMBER_SUCCESS if the hash was * calculated successfully. EMBER_INVALID_CALL if the block size is not a * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the * data exceeds the maximum limits of the hash function. * @returns result context or null */ - private async emberAesMmoHashFinal(context: EmberAesMmoHashContext, data: Buffer): Promise<[EmberStatus, reContext: EmberAesMmoHashContext]> { + private async emberAesMmoHashFinal(context: EmberAesMmoHashContext, data: Buffer): Promise<[SLStatus, reContext: EmberAesMmoHashContext]> { return this.aesMmoHash(context, true/*finalize?*/, data); } @@ -1865,13 +1847,13 @@ export class EmberAdapter extends Adapter { * * @param data const uint8_t* The data to hash. Expected of valid length (as in, not larger alloc) * - * @returns An ::EmberStatus value indicating EMBER_SUCCESS if the hash was + * @returns An ::SLStatus value indicating EMBER_SUCCESS if the hash was * calculated successfully. EMBER_INVALID_CALL if the block size is not a * multiple of 16 bytes, and EMBER_INDEX_OUT_OF_RANGE is returned when the * data exceeds the maximum limits of the hash function. * @returns result uint8_t* The location where the result of the hash will be written. */ - private async emberAesHashSimple(data: Buffer): Promise<[EmberStatus, result: Buffer]> { + private async emberAesHashSimple(data: Buffer): Promise<[SLStatus, result: Buffer]> { const context = aesMmoHashInit(); const [status, reContext] = (await this.emberAesMmoHashFinal(context, data)); @@ -1893,7 +1875,7 @@ export class EmberAdapter extends Adapter { * @returns messageTag The tag passed to ezspSend${x} function. */ private async emberPermitJoining(duration: number, broadcastMgmtPermitJoin: boolean) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { let status = (await this.ezsp.ezspPermitJoining(duration)); let apsFrame: EmberApsFrame = null; let messageTag: number = null; @@ -1913,7 +1895,7 @@ export class EmberAdapter extends Adapter { * @param decision * @returns */ - private async emberSetJoinPolicy(decision: EmberJoinDecision): Promise { + private async emberSetJoinPolicy(decision: EmberJoinDecision): Promise { let policy: number = EzspDecisionBitmask.DEFAULT_CONFIGURATION; if (decision == EmberJoinDecision.USE_PRECONFIGURED_KEY) { @@ -1927,91 +1909,6 @@ export class EmberAdapter extends Adapter { return this.emberSetEzspPolicy(EzspPolicyId.TRUST_CENTER_POLICY, policy); } - /** - * Get Source Route Overhead - * - * Returns the number of bytes needed in a packet for source routing. - * Since each hop consumes 2 bytes in the packet, this routine calculates the - * total number of bytes needed based on number of hops to reach the destination. - * - * This function is called by the framework to determine the overhead required - * in the network frame for source routing to a particular destination. - * - * @param destination The node id of the destination Ver.: always - * @returns int8u The number of bytes needed for source routing in a packet. - */ - public async emberGetSourceRouteOverhead(destination: EmberNodeId): Promise { - const [status, value] = (await this.ezsp.ezspGetSourceRouteOverhead(destination)); - - if (status === EzspStatus.SUCCESS) { - return value; - } else { - logger.debug(`Failed to get source route overhead (via extended value), status=${EzspStatus[status]}.`, NS); - } - - return 0; - } - - /** - * Return the maximum size of the payload that the Application Support sub-layer will accept for - * the given message type, destination, and APS frame. - * - * The size depends on multiple factors, including the security level in use and additional information - * added to the message to support the various options. - * - * @param type The outgoing message type. - * @param indexOrDestination uint16_t Depending on the message type, this is either the - * EmberNodeId of the destination, an index into the address table, an index - * into the binding table, the multicast identifier, or a broadcast address. - * @param apsFrame EmberApsFrame *The APS frame for the message. - * @return uint8_t The maximum APS payload length for the given message. - */ - private async maximumApsPayloadLength(type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame): Promise { - let destination: EmberNodeId = EMBER_UNKNOWN_NODE_ID; - let max: number = MAXIMUM_APS_PAYLOAD_LENGTH;// uint8_t - - if ((apsFrame.options & EmberApsOption.ENCRYPTION) !== 0) { - max -= APS_ENCRYPTION_OVERHEAD; - } - - if ((apsFrame.options & EmberApsOption.SOURCE_EUI64) !== 0) { - max -= EUI64_SIZE; - } - - if ((apsFrame.options & EmberApsOption.DESTINATION_EUI64) !== 0) { - max -= EUI64_SIZE; - } - - if ((apsFrame.options & EmberApsOption.FRAGMENT) !== 0) { - max -= APS_FRAGMENTATION_OVERHEAD; - } - - switch (type) { - case EmberOutgoingMessageType.DIRECT: - destination = indexOrDestination; - break; - case EmberOutgoingMessageType.VIA_ADDRESS_TABLE: - destination = (await this.ezsp.ezspGetAddressTableRemoteNodeId(indexOrDestination)); - break; - case EmberOutgoingMessageType.VIA_BINDING: - destination = (await this.ezsp.ezspGetBindingRemoteNodeId(indexOrDestination)); - break; - case EmberOutgoingMessageType.MULTICAST: - // APS multicast messages include the two-byte group id and exclude the one-byte destination endpoint, - // for a net loss of an extra byte. - max--; - break; - case EmberOutgoingMessageType.BROADCAST: - break; - default: - break; - } - - max -= (await this.emberGetSourceRouteOverhead(destination)); - - return max; - } - //---- END EZSP wrappers //---- START Ember ZDO @@ -2061,10 +1958,10 @@ export class EmberAdapter extends Adapter { * @returns apsFrame The APS Frame resulting of the request being built and sent (`sequence` set from stack-given value). * @returns messageTag The tag passed to ezspSend${x} function. */ - private async sendZDORequestBuffer(destination: EmberNodeId, clusterId: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async sendZDORequestBuffer(destination: NodeId, clusterId: number, options: EmberApsOption): + Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { if (this.zdoRequestBuffalo.getPosition() > EZSP_MAX_FRAME_LENGTH) { - return [EmberStatus.MESSAGE_TOO_LONG, null, null]; + return [SLStatus.MESSAGE_TOO_LONG, null, null]; } const messageTag = this.nextZDORequestSequence(); @@ -2086,7 +1983,9 @@ export class EmberAdapter extends Adapter { || destination === BroadcastAddress.SLEEPY) { logger.debug(`~~~> [ZDO BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS); const [status, apsSequence] = (await this.ezsp.ezspSendBroadcast( + ZSpec.NULL_NODE_ID,// alias destination, + 0,// nwkSequence apsFrame, this.getZDORequestRadius(), messageTag, @@ -2095,7 +1994,7 @@ export class EmberAdapter extends Adapter { apsFrame.sequence = apsSequence; logger.debug( - `~~~> [SENT ZDO type=BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${EmberStatus[status]}]`, + `~~~> [SENT ZDO type=BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${SLStatus[status]}]`, NS, ); return [status, apsFrame, messageTag]; @@ -2111,7 +2010,7 @@ export class EmberAdapter extends Adapter { apsFrame.sequence = apsSequence; logger.debug( - `~~~> [SENT ZDO type=DIRECT apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${EmberStatus[status]}]`, + `~~~> [SENT ZDO type=DIRECT apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${SLStatus[status]}]`, NS, ); return [status, apsFrame, messageTag]; @@ -2138,18 +2037,18 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the unicast request. See * emberSendUnicast() for a description. This parameter is ignored if the target * is a broadcast address. - * @returns An EmberStatus value. EMBER_SUCCESS, MESSAGE_TOO_LONG, + * @returns An SLStatus value. EMBER_SUCCESS, MESSAGE_TOO_LONG, * EMBER_NETWORK_DOWN or EMBER_NETWORK_BUSY. */ - private async emberMatchDescriptorsRequest(target: EmberNodeId, profile: number, inClusters: number[], outClusters: number[], - options: EmberApsOption): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberMatchDescriptorsRequest(target: NodeId, profile: number, inClusters: number[], outClusters: number[], + options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { // 2 bytes for NWK Address + 2 bytes for Profile Id + 1 byte for in Cluster Count // + in times 2 for 2 byte Clusters + out Cluster Count + out times 2 for 2 byte Clusters const length = (ZDO_MESSAGE_OVERHEAD + 2 + 2 + 1 + (inClusters.length * 2) + 1 + (outClusters.length * 2)); // sanity check if (length > EZSP_MAX_FRAME_LENGTH) { - return [EmberStatus.MESSAGE_TOO_LONG, null, null]; + return [SLStatus.MESSAGE_TOO_LONG, null, null]; } this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); @@ -2179,14 +2078,14 @@ export class EmberAdapter extends Adapter { * @param childStartIndex uint8_t The index of the first child to list in the response. * Ignored if @c reportKids is false. * - * @return An ::EmberStatus value. + * @return An ::SLStatus value. * - ::EMBER_SUCCESS - The request was transmitted successfully. * - ::EMBER_NO_BUFFERS - Insufficient message buffers were available to construct the request. * - ::EMBER_NETWORK_DOWN - The node is not part of a network. * - ::EMBER_NETWORK_BUSY - Transmission of the request failed. */ - private async emberNetworkAddressRequest(target: EmberEUI64, reportKids: boolean, childStartIndex: number) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberNetworkAddressRequest(target: EUI64, reportKids: boolean, childStartIndex: number) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(target); @@ -2209,14 +2108,14 @@ export class EmberAdapter extends Adapter { * Ignored if reportKids is false. * @param options The options to use when sending the request. See ::emberSendUnicast() for a description. * - * @return An ::EmberStatus value. + * @return An ::SLStatus value. * - ::EMBER_SUCCESS * - ::EMBER_NO_BUFFERS * - ::EMBER_NETWORK_DOWN * - ::EMBER_NETWORK_BUSY */ - private async emberIeeeAddressRequest(target: EmberNodeId, reportKids: boolean, childStartIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberIeeeAddressRequest(target: NodeId, reportKids: boolean, childStartIndex: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); @@ -2235,8 +2134,8 @@ export class EmberAdapter extends Adapter { * @param options * @param targetNodeIdOfRequest */ - private async emberIeeeAddressRequestToTarget(discoveryNodeId: EmberNodeId, reportKids: boolean, childStartIndex: number, - options: EmberApsOption, targetNodeIdOfRequest: EmberNodeId): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberIeeeAddressRequestToTarget(discoveryNodeId: NodeId, reportKids: boolean, childStartIndex: number, + options: EmberApsOption, targetNodeIdOfRequest: NodeId): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(discoveryNodeId); @@ -2256,8 +2155,8 @@ export class EmberAdapter extends Adapter { * @param options * @returns */ - private async emberSendZigDevRequestTarget(target: EmberNodeId, clusterId: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberSendZigDevRequestTarget(target: NodeId, clusterId: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); @@ -2281,11 +2180,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberSimpleDescriptorRequest(target: EmberNodeId, targetEndpoint: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberSimpleDescriptorRequest(target: NodeId, targetEndpoint: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); @@ -2310,7 +2209,7 @@ export class EmberAdapter extends Adapter { * @param destinationEndpoint * @param options * - * @returns An ::EmberStatus value. + * @returns An ::SLStatus value. * - ::EMBER_SUCCESS * - ::EMBER_NO_BUFFERS * - ::EMBER_NETWORK_DOWN @@ -2318,9 +2217,9 @@ export class EmberAdapter extends Adapter { * @returns APS frame created for the request * @returns The tag used on the message. */ - private async emberSendZigDevBindRequest(target: EmberNodeId, bindClusterId: number, source: EmberEUI64, sourceEndpoint: number, - clusterId: number, type: number, destination: EmberEUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, - options: EmberApsOption): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberSendZigDevBindRequest(target: NodeId, bindClusterId: number, source: EUI64, sourceEndpoint: number, + clusterId: number, type: number, destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, + options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(source); @@ -2337,7 +2236,7 @@ export class EmberAdapter extends Adapter { this.zdoRequestBuffalo.writeUInt16(groupAddress); break; default: - return [EmberStatus.ERR_FATAL, null, null]; + return [SLStatus.FAIL, null, null]; } return this.sendZDORequestBuffer(target, bindClusterId, options); @@ -2364,7 +2263,7 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @returns An ::EmberStatus value. + * @returns An ::SLStatus value. * - ::EMBER_SUCCESS * - ::EMBER_NO_BUFFERS * - ::EMBER_NETWORK_DOWN @@ -2372,9 +2271,9 @@ export class EmberAdapter extends Adapter { * @returns APS frame created for the request * @returns The tag used on the message. */ - private async emberBindRequest(target: EmberNodeId, source: EmberEUI64, sourceEndpoint: number, clusterId: number, type: number, - destination: EmberEUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberBindRequest(target: NodeId, source: EUI64, sourceEndpoint: number, clusterId: number, type: number, + destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO BIND_REQUEST target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`, NS); return this.emberSendZigDevBindRequest( @@ -2412,7 +2311,7 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @returns An ::EmberStatus value. + * @returns An ::SLStatus value. * - ::EMBER_SUCCESS * - ::EMBER_NO_BUFFERS * - ::EMBER_NETWORK_DOWN @@ -2420,9 +2319,9 @@ export class EmberAdapter extends Adapter { * @returns APS frame created for the request * @returns The tag used on the message. */ - private async emberUnbindRequest(target: EmberNodeId, source: EmberEUI64, sourceEndpoint: number, clusterId: number, type: number, - destination: EmberEUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberUnbindRequest(target: NodeId, source: EUI64, sourceEndpoint: number, clusterId: number, type: number, + destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug( `~~~> [ZDO UNBIND_REQUEST target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`, @@ -2452,11 +2351,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberActiveEndpointsRequest(target: EmberNodeId, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberActiveEndpointsRequest(target: NodeId, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO ACTIVE_ENDPOINTS_REQUEST target=${target}]`, NS); return this.emberSendZigDevRequestTarget(target, ACTIVE_ENDPOINTS_REQUEST, options); } @@ -2474,11 +2373,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberPowerDescriptorRequest(target: EmberNodeId, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberPowerDescriptorRequest(target: NodeId, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO POWER_DESCRIPTOR_REQUEST target=${target}]`, NS); return this.emberSendZigDevRequestTarget(target, POWER_DESCRIPTOR_REQUEST, options); } @@ -2495,11 +2394,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An ::EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An ::SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberNodeDescriptorRequest(target: EmberNodeId, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberNodeDescriptorRequest(target: NodeId, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO NODE_DESCRIPTOR_REQUEST target=${target}]`, NS); return this.emberSendZigDevRequestTarget(target, NODE_DESCRIPTOR_REQUEST, options); } @@ -2518,11 +2417,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberLqiTableRequest(target: EmberNodeId, startIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberLqiTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO LQI_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); return this.emberTableRequest(LQI_TABLE_REQUEST, target, startIndex, options); } @@ -2541,11 +2440,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberRoutingTableRequest(target: EmberNodeId, startIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberRoutingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO ROUTING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); return this.emberTableRequest(ROUTING_TABLE_REQUEST, target, startIndex, options); } @@ -2565,11 +2464,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberBindingTableRequest(target: EmberNodeId, startIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberBindingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO BINDING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); return this.emberTableRequest(BINDING_TABLE_REQUEST, target, startIndex, options); } @@ -2583,8 +2482,8 @@ export class EmberAdapter extends Adapter { * @param options * @returns */ - private async emberTableRequest(clusterId: number, target: EmberNodeId, startIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberTableRequest(clusterId: number, target: NodeId, startIndex: number, options: EmberApsOption) + : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt8(startIndex); @@ -2607,11 +2506,11 @@ export class EmberAdapter extends Adapter { * @param options The options to use when sending the request. See * emberSendUnicast() for a description. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberLeaveRequest(target: EmberNodeId, deviceAddress: EmberEUI64, leaveRequestFlags: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberLeaveRequest(target: NodeId, deviceAddress: EUI64, leaveRequestFlags: number, options: EmberApsOption): + Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(deviceAddress); @@ -2635,11 +2534,11 @@ export class EmberAdapter extends Adapter { * emberSendUnicast() for a description. This parameter is ignored if the target * is a broadcast address. * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, + * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. */ - private async emberPermitJoiningRequest(target: EmberNodeId, duration: number, authentication: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberPermitJoiningRequest(target: NodeId, duration: number, authentication: number, options: EmberApsOption): + Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt8(duration); @@ -2660,8 +2559,8 @@ export class EmberAdapter extends Adapter { * @param count uint8_t * @param manager */ - private async emberNetworkUpdateRequest(target: EmberNodeId, scanChannels: number[], duration: number, count: number | null, - manager: EmberNodeId | null, options: EmberApsOption): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberNetworkUpdateRequest(target: NodeId, scanChannels: number[], duration: number, count: number | null, + manager: NodeId | null, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt32(scanChannels.reduce((a, c) => a + (1 << c), 0));// to uint32_t @@ -2682,18 +2581,18 @@ export class EmberAdapter extends Adapter { return this.sendZDORequestBuffer(target, NWK_UPDATE_REQUEST, options); } - private async emberScanChannelsRequest(target: EmberNodeId, scanChannels: number[], duration: number, count: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberScanChannelsRequest(target: NodeId, scanChannels: number[], duration: number, count: number, options: EmberApsOption): + Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { return this.emberNetworkUpdateRequest(target, scanChannels, duration, count, null, options); } - private async emberChannelChangeRequest(target: EmberNodeId, channel: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberChannelChangeRequest(target: NodeId, channel: number, options: EmberApsOption): + Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { return this.emberNetworkUpdateRequest(target, [channel], 0xFE, null, null, options); } - private async emberSetActiveChannelsAndNwkManagerIdRequest(target: EmberNodeId, scanChannels: number[], manager: EmberNodeId, - options: EmberApsOption): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { + private async emberSetActiveChannelsAndNwkManagerIdRequest(target: NodeId, scanChannels: number[], manager: NodeId, + options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { return this.emberNetworkUpdateRequest(target, scanChannels, 0xFF, null, manager, options); } @@ -2742,7 +2641,7 @@ export class EmberAdapter extends Adapter { public async getCoordinator(): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // in all likelihood this will be retrieved from cache @@ -2763,7 +2662,7 @@ export class EmberAdapter extends Adapter { }), }); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -2792,12 +2691,12 @@ export class EmberAdapter extends Adapter { public async backup(ieeeAddressesInDatabase: string[]): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { // grab fresh version here, bypass cache const [netStatus, , netParams] = (await this.ezsp.ezspGetNetworkParameters()); - if (netStatus !== EmberStatus.SUCCESS) { - logger.error(`[BACKUP] Failed to get network parameters.`, NS); + if (netStatus !== SLStatus.OK) { + logger.error(`[BACKUP] Failed to get network parameters with status=${SLStatus[netStatus]}.`, NS); return netStatus; } @@ -2808,9 +2707,8 @@ export class EmberAdapter extends Adapter { const [netKeyStatus, netKeyInfo] = (await this.ezsp.ezspGetNetworkKeyInfo()); if (netKeyStatus !== SLStatus.OK) { - logger.error(`[BACKUP] Failed to get network keys info.`, NS); - return ((netKeyStatus === SLStatus.BUSY) || (netKeyStatus === SLStatus.NOT_READY)) - ? EmberStatus.NETWORK_BUSY : EmberStatus.ERR_FATAL;// allow retry on statuses that should be temporary + logger.error(`[BACKUP] Failed to get network keys info with status=${SLStatus[netKeyStatus]}.`, NS); + return netKeyStatus; } if (!netKeyInfo.networkKeySet) { @@ -2831,7 +2729,7 @@ export class EmberAdapter extends Adapter { let context: SecManContext = initSecurityManagerContext(); context.coreKeyType = SecManKeyType.TC_LINK; - const [tcLinkKey, tclkStatus] = (await this.ezsp.ezspExportKey(context)); + const [tclkStatus, tcLinkKey] = (await this.ezsp.ezspExportKey(context)); if (tclkStatus !== SLStatus.OK) { throw new Error(`[BACKUP] Failed to export TC Link Key with status=${SLStatus[tclkStatus]}.`); @@ -2840,7 +2738,7 @@ export class EmberAdapter extends Adapter { context = initSecurityManagerContext();// make sure it's back to zeroes context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 0; - const [networkKey, nkStatus] = (await this.ezsp.ezspExportKey(context)); + const [nkStatus, networkKey] = (await this.ezsp.ezspExportKey(context)); if (nkStatus !== SLStatus.OK) { throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); @@ -2882,7 +2780,7 @@ export class EmberAdapter extends Adapter { } }); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, true,/*prioritize*/ @@ -2895,7 +2793,7 @@ export class EmberAdapter extends Adapter { public async getNetworkParameters(): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // first call will cache for the others, but in all likelihood, it will all be from freshly cached after init @@ -2910,7 +2808,7 @@ export class EmberAdapter extends Adapter { channel, }); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -2925,7 +2823,7 @@ export class EmberAdapter extends Adapter { public async changeChannel(newChannel: number): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -2935,8 +2833,8 @@ export class EmberAdapter extends Adapter { DEFAULT_APS_OPTIONS, )); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed broadcast channel change to "${newChannel}" with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed broadcast channel change to "${newChannel}" with status=${SLStatus[status]}.`, NS); return status; } @@ -2947,7 +2845,7 @@ export class EmberAdapter extends Adapter { ); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -2963,16 +2861,16 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const status = await this.ezsp.ezspSetRadioPower(value); - if (status !== EmberStatus.SUCCESS) { - logger.error(`Failed to set transmit power to ${value} status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`Failed to set transmit power to ${value} status=${SLStatus[status]}.`, NS); return status; } resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3011,28 +2909,28 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { // Compute the key from the install code and CRC. const [aesStatus, keyContents] = (await this.emberAesHashSimple(key)); - if (aesStatus !== EmberStatus.SUCCESS) { - logger.error(`[ADD INSTALL CODE] Failed AES hash for '${ieeeAddress}' with status=${EmberStatus[aesStatus]}.`, NS); + if (aesStatus !== SLStatus.OK) { + logger.error(`[ADD INSTALL CODE] Failed AES hash for '${ieeeAddress}' with status=${SLStatus[aesStatus]}.`, NS); return aesStatus; } // Add the key to the transient key table. // This will be used while the DUT joins. - const impStatus = (await this.ezsp.ezspImportTransientKey(ieeeAddress, {contents: keyContents}, SecManFlag.NONE)); + const impStatus = (await this.ezsp.ezspImportTransientKey(ieeeAddress as EUI64, {contents: keyContents})); if (impStatus == SLStatus.OK) { logger.debug(`[ADD INSTALL CODE] Success for '${ieeeAddress}'.`, NS); } else { logger.error(`[ADD INSTALL CODE] Failed for '${ieeeAddress}' with status=${SLStatus[impStatus]}.`, NS); - return EmberStatus.ERR_FATAL; + return SLStatus.FAIL; } resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3068,24 +2966,24 @@ export class EmberAdapter extends Adapter { // queued, non-InterPAN public async permitJoin(seconds: number, networkAddress: number): Promise { - const preJoining = async (): Promise => { + const preJoining = async (): Promise => { if (seconds) { const plaintextKey: SecManKey = {contents: Buffer.from(ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY)}; - const impKeyStatus = (await this.ezsp.ezspImportTransientKey(ZSpec.BLANK_EUI64, plaintextKey, SecManFlag.NONE)); + const impKeyStatus = (await this.ezsp.ezspImportTransientKey(ZSpec.BLANK_EUI64, plaintextKey)); if (impKeyStatus !== SLStatus.OK) { logger.error(`[ZDO] Failed import transient key with status=${SLStatus[impKeyStatus]}.`, NS); - return EmberStatus.ERR_FATAL; + return SLStatus.FAIL; } const setJPstatus = (await this.emberSetJoinPolicy(EmberJoinDecision.USE_PRECONFIGURED_KEY)); - if (setJPstatus !== EzspStatus.SUCCESS) { - logger.error(`[ZDO] Failed set join policy with status=${EzspStatus[setJPstatus]}.`, NS); - return EmberStatus.ERR_FATAL; + if (setJPstatus !== SLStatus.OK) { + logger.error(`[ZDO] Failed set join policy with status=${SLStatus[setJPstatus]}.`, NS); + return SLStatus.FAIL; } - return EmberStatus.SUCCESS; + return SLStatus.OK; } else { if (this.manufacturerCode !== DEFAULT_MANUFACTURER_CODE) { logger.debug(`[WORKAROUND] Reverting coordinator manufacturer code to default.`, NS); @@ -3098,12 +2996,12 @@ export class EmberAdapter extends Adapter { const setJPstatus = (await this.emberSetJoinPolicy(EmberJoinDecision.ALLOW_REJOINS_ONLY)); - if (setJPstatus !== EzspStatus.SUCCESS) { - logger.error(`[ZDO] Failed set join policy for with status=${EzspStatus[setJPstatus]}.`, NS); - return EmberStatus.ERR_FATAL; + if (setJPstatus !== SLStatus.OK) { + logger.error(`[ZDO] Failed set join policy for with status=${SLStatus[setJPstatus]}.`, NS); + return SLStatus.FAIL; } - return EmberStatus.SUCCESS; + return SLStatus.OK; } }; @@ -3111,13 +3009,13 @@ export class EmberAdapter extends Adapter { // specific device that is not `Coordinator` return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); const pjStatus = (await preJoining()); - if (pjStatus !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${EmberStatus[pjStatus]}.`, NS); + if (pjStatus !== SLStatus.OK) { + logger.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${SLStatus[pjStatus]}.`, NS); return pjStatus; } @@ -3125,8 +3023,8 @@ export class EmberAdapter extends Adapter { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(networkAddress, seconds, 1, 0)); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed permit joining request for "${networkAddress}" with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed permit joining request for "${networkAddress}" with status=${SLStatus[status]}.`, NS); return status; } @@ -3137,7 +3035,7 @@ export class EmberAdapter extends Adapter { }, DEFAULT_ZDO_REQUEST_TIMEOUT)); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3146,13 +3044,13 @@ export class EmberAdapter extends Adapter { // coordinator-only, or all return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); const pjStatus = (await preJoining()); - if (pjStatus !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${EmberStatus[pjStatus]}.`, NS); + if (pjStatus !== SLStatus.OK) { + logger.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${SLStatus[pjStatus]}.`, NS); return pjStatus; } @@ -3163,8 +3061,8 @@ export class EmberAdapter extends Adapter { (networkAddress === ZSpec.COORDINATOR_ADDRESS) ? false : true, )); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed permit joining request with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed permit joining request with status=${SLStatus[status]}.`, NS); return status; } @@ -3187,7 +3085,7 @@ export class EmberAdapter extends Adapter { // } resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3199,12 +3097,12 @@ export class EmberAdapter extends Adapter { public async lqi(networkAddress: number): Promise { const neighbors: TsType.LQINeighbor[] = []; - const request = async (startIndex: number): Promise<[EmberStatus, tableEntries: number, entryCount: number]> => { + const request = async (startIndex: number): Promise<[SLStatus, tableEntries: number, entryCount: number]> => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [reqStatus, apsFrame, messageTag] = (await this.emberLqiTableRequest(networkAddress, startIndex, DEFAULT_APS_OPTIONS)); - if (reqStatus !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${EmberStatus[reqStatus]}.`, NS); + if (reqStatus !== SLStatus.OK) { + logger.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[reqStatus]}.`, NS); return [reqStatus, null, null]; } @@ -3224,17 +3122,17 @@ export class EmberAdapter extends Adapter { }); } - return [EmberStatus.SUCCESS, result.neighborTableEntries, result.entryList.length]; + return [SLStatus.OK, result.neighborTableEntries, result.entryList.length]; }; return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); let [status, tableEntries, entryCount] = (await request(0)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -3244,7 +3142,7 @@ export class EmberAdapter extends Adapter { while (neighbors.length < size) { [status, tableEntries, entryCount] = (await request(nextStartIndex)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -3263,13 +3161,13 @@ export class EmberAdapter extends Adapter { public async routingTable(networkAddress: number): Promise { const table: TsType.RoutingTableEntry[] = []; - const request = async (startIndex: number): Promise<[EmberStatus, tableEntries: number, entryCount: number]> => { + const request = async (startIndex: number): Promise<[SLStatus, tableEntries: number, entryCount: number]> => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [reqStatus, apsFrame, messageTag] = (await this.emberRoutingTableRequest(networkAddress, startIndex, DEFAULT_APS_OPTIONS)); - if (reqStatus !== EmberStatus.SUCCESS) { + if (reqStatus !== SLStatus.OK) { logger.error( - `[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${EmberStatus[reqStatus]}.`, + `[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[reqStatus]}.`, NS, ); return [reqStatus, null, null]; @@ -3289,17 +3187,17 @@ export class EmberAdapter extends Adapter { }); } - return [EmberStatus.SUCCESS, result.routingTableEntries, result.entryList.length]; + return [SLStatus.OK, result.routingTableEntries, result.entryList.length]; }; return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); let [status, tableEntries, entryCount] = (await request(0)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -3309,7 +3207,7 @@ export class EmberAdapter extends Adapter { while (table.length < size) { [status, tableEntries, entryCount] = (await request(nextStartIndex)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -3317,7 +3215,7 @@ export class EmberAdapter extends Adapter { } resolve({table}); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3328,14 +3226,14 @@ export class EmberAdapter extends Adapter { public async nodeDescriptor(networkAddress: number): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); /* eslint-disable @typescript-eslint/no-unused-vars */ const [status, apsFrame, messageTag] = (await this.emberNodeDescriptorRequest(networkAddress, DEFAULT_APS_OPTIONS)); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed node descriptor for "${networkAddress}" with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed node descriptor for "${networkAddress}" with status=${SLStatus[status]}.`, NS); return status; } @@ -3368,7 +3266,7 @@ export class EmberAdapter extends Adapter { resolve({type, manufacturerCode: result.manufacturerCode}); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3379,14 +3277,14 @@ export class EmberAdapter extends Adapter { public async activeEndpoints(networkAddress: number): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberActiveEndpointsRequest(networkAddress, DEFAULT_APS_OPTIONS)); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[ZDO] Failed active endpoints request for "${networkAddress}" with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed active endpoints request for "${networkAddress}" with status=${SLStatus[status]}.`, NS); return status; } @@ -3398,7 +3296,7 @@ export class EmberAdapter extends Adapter { resolve({endpoints: result.endpointList}); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3409,7 +3307,7 @@ export class EmberAdapter extends Adapter { public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -3419,9 +3317,9 @@ export class EmberAdapter extends Adapter { DEFAULT_APS_OPTIONS )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed simple descriptor request for "${networkAddress}" endpoint "${endpointID}" ` - + `with status=${EmberStatus[status]}.`, NS); + + `with status=${SLStatus[status]}.`, NS); return status; } @@ -3439,7 +3337,7 @@ export class EmberAdapter extends Adapter { outputClusters: result.outClusterList, }); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3453,25 +3351,25 @@ export class EmberAdapter extends Adapter { // dest address is EUI64 (str), so type should always be endpoint (unicast) return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberBindRequest( destinationNetworkAddress, - sourceIeeeAddress, + sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, UNICAST_BINDING, - destinationAddressOrGroup, + destinationAddressOrGroup as EUI64, null,// doesn't matter destinationEndpoint, DEFAULT_APS_OPTIONS, )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` - + `endpoint "${destinationEndpoint}" with status=${EmberStatus[status]}.`, NS); + + `endpoint "${destinationEndpoint}" with status=${SLStatus[status]}.`, NS); return status; } @@ -3482,7 +3380,7 @@ export class EmberAdapter extends Adapter { }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3491,13 +3389,13 @@ export class EmberAdapter extends Adapter { // dest is group num, so type should always be group (multicast) return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberBindRequest( destinationNetworkAddress, - sourceIeeeAddress, + sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, MULTICAST_BINDING, @@ -3507,9 +3405,9 @@ export class EmberAdapter extends Adapter { DEFAULT_APS_OPTIONS, )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` - + `with status=${EmberStatus[status]}.`, NS); + + `with status=${SLStatus[status]}.`, NS); return status; } @@ -3521,7 +3419,7 @@ export class EmberAdapter extends Adapter { resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3536,25 +3434,25 @@ export class EmberAdapter extends Adapter { // dest address is EUI64 (str), so type should always be endpoint (unicast) return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberUnbindRequest( destinationNetworkAddress, - sourceIeeeAddress, + sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, UNICAST_BINDING, - destinationAddressOrGroup, + destinationAddressOrGroup as EUI64, null,// doesn't matter destinationEndpoint, DEFAULT_APS_OPTIONS, )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` - + `endpoint "${destinationEndpoint}" with status=${EmberStatus[status]}.`, NS); + + `endpoint "${destinationEndpoint}" with status=${SLStatus[status]}.`, NS); return status; } @@ -3566,7 +3464,7 @@ export class EmberAdapter extends Adapter { resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3575,13 +3473,13 @@ export class EmberAdapter extends Adapter { // dest is group num, so type should always be group (multicast) return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberUnbindRequest( destinationNetworkAddress, - sourceIeeeAddress, + sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, MULTICAST_BINDING, @@ -3591,9 +3489,9 @@ export class EmberAdapter extends Adapter { DEFAULT_APS_OPTIONS, )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` - + `with status=${EmberStatus[status]}.`, NS); + + `with status=${SLStatus[status]}.`, NS); return status; } @@ -3605,7 +3503,7 @@ export class EmberAdapter extends Adapter { resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3617,20 +3515,20 @@ export class EmberAdapter extends Adapter { public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberLeaveRequest( networkAddress, - ieeeAddr, + ieeeAddr as EUI64, EmberLeaveRequestFlags.WITHOUT_REJOIN, DEFAULT_APS_OPTIONS )); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed remove device request for "${networkAddress}" target "${ieeeAddr}" ` - + `with status=${EmberStatus[status]}.`, NS); + + `with status=${SLStatus[status]}.`, NS); return status; } @@ -3642,7 +3540,7 @@ export class EmberAdapter extends Adapter { resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3684,19 +3582,9 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); - if (CHECK_APS_PAYLOAD_LENGTH) { - const maxPayloadLength = ( - await this.maximumApsPayloadLength(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame) - ); - - if (data.length > maxPayloadLength) { - return EmberStatus.MESSAGE_TOO_LONG;// queue will reject - } - } - logger.debug( `~~~> [ZCL to=${networkAddress} apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.header)}]`, NS, @@ -3711,8 +3599,8 @@ export class EmberAdapter extends Adapter { 0,// alias seq )); - if (status !== EmberStatus.SUCCESS) { - logger.error(`~x~> [ZCL to=${networkAddress}] Failed to send request with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`~x~> [ZCL to=${networkAddress}] Failed to send request with status=${SLStatus[status]}.`, NS); return status;// let queue handle retry based on status } @@ -3728,7 +3616,7 @@ export class EmberAdapter extends Adapter { resolve(result); } else { resolve(null);// don't expect a response - return EmberStatus.SUCCESS; + return SLStatus.OK; } }, reject, @@ -3753,19 +3641,9 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); - if (CHECK_APS_PAYLOAD_LENGTH) { - const maxPayloadLength = ( - await this.maximumApsPayloadLength(EmberOutgoingMessageType.MULTICAST, groupID, apsFrame) - ); - - if (data.length > maxPayloadLength) { - return EmberStatus.MESSAGE_TOO_LONG;// queue will reject - } - } - logger.debug(`~~~> [ZCL GROUP apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.header)}]`, NS); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, messageTag] = (await this.ezsp.send( @@ -3777,15 +3655,15 @@ export class EmberAdapter extends Adapter { 0,// alias seq )); - if (status !== EmberStatus.SUCCESS) { - logger.error(`~x~> [ZCL GROUP] Failed to send with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`~x~> [ZCL GROUP] Failed to send with status=${SLStatus[status]}.`, NS); return status;// let queue handle retry based on status } // NOTE: since ezspMessageSentHandler could take a while here, we don't block, it'll just be logged if the delivery failed resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3809,19 +3687,9 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.checkInterpanLock(); - if (CHECK_APS_PAYLOAD_LENGTH) { - const maxPayloadLength = ( - await this.maximumApsPayloadLength(EmberOutgoingMessageType.BROADCAST, destination, apsFrame) - ); - - if (data.length > maxPayloadLength) { - return EmberStatus.MESSAGE_TOO_LONG;// queue will reject - } - } - logger.debug(`~~~> [ZCL BROADCAST apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.header)}]`, NS); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, messageTag] = (await this.ezsp.send( @@ -3833,15 +3701,15 @@ export class EmberAdapter extends Adapter { 0,// alias seq )); - if (status !== EmberStatus.SUCCESS) { - logger.error(`~x~> [ZCL BROADCAST] Failed to send with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`~x~> [ZCL BROADCAST] Failed to send with status=${SLStatus[status]}.`, NS); return status;// let queue handle retry based on status } // NOTE: since ezspMessageSentHandler could take a while here, we don't block, it'll just be logged if the delivery failed resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3861,13 +3729,13 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { this.interpanLock = true; const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(channel)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { this.interpanLock = false;// XXX: ok? - logger.error(`Failed to set InterPAN channel to ${channel} with status=${EmberStatus[status]}.`, NS); + logger.error(`Failed to set InterPAN channel to ${channel} with status=${SLStatus[status]}.`, NS); return status; } @@ -3883,7 +3751,7 @@ export class EmberAdapter extends Adapter { public async sendZclFrameInterPANToIeeeAddr(zclFrame: Zcl.Frame, ieeeAddress: string): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const msgBuffalo = new EzspBuffalo(Buffer.alloc(MAXIMUM_INTERPAN_LENGTH)); // cache-enabled getters @@ -3902,10 +3770,14 @@ export class EmberAdapter extends Adapter { msgBuffalo.writeUInt16(ZSpec.TOUCHLINK_PROFILE_ID); logger.debug(`~~~> [ZCL TOUCHLINK to=${ieeeAddress} header=${JSON.stringify(zclFrame.header)}]`, NS); - const status = (await this.ezsp.ezspSendRawMessage(Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]))); + const status = await this.ezsp.ezspSendRawMessage( + Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]), + EmberTransmitPriority.NORMAL, + true + ); - if (status !== EmberStatus.SUCCESS) { - logger.error(`~x~> [ZCL TOUCHLINK to=${ieeeAddress}] Failed to send with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`~x~> [ZCL TOUCHLINK to=${ieeeAddress}] Failed to send with status=${SLStatus[status]}.`, NS); return status; } @@ -3940,7 +3812,7 @@ export class EmberAdapter extends Adapter { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const msgBuffalo = new EzspBuffalo(Buffer.alloc(MAXIMUM_INTERPAN_LENGTH)); // cache-enabled getters @@ -3961,10 +3833,10 @@ export class EmberAdapter extends Adapter { const data = Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]); logger.debug(`~~~> [ZCL TOUCHLINK BROADCAST header=${JSON.stringify(zclFrame.header)}]`, NS); - const status = (await this.ezsp.ezspSendRawMessage(data)); + const status = (await this.ezsp.ezspSendRawMessage(data, EmberTransmitPriority.NORMAL, true)); - if (status !== EmberStatus.SUCCESS) { - logger.error(`~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${SLStatus[status]}.`, NS); return status; } @@ -3979,7 +3851,7 @@ export class EmberAdapter extends Adapter { resolve(result); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ); @@ -3990,12 +3862,12 @@ export class EmberAdapter extends Adapter { public async restoreChannelInterPAN(): Promise { return new Promise((resolve, reject): void => { this.requestQueue.enqueue( - async (): Promise => { + async (): Promise => { const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(this.networkOptions.channelList[0])); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error( - `Failed to restore InterPAN channel to ${this.networkOptions.channelList[0]} with status=${EmberStatus[status]}.`, + `Failed to restore InterPAN channel to ${this.networkOptions.channelList[0]} with status=${SLStatus[status]}.`, NS, ); return status; @@ -4021,7 +3893,7 @@ export class EmberAdapter extends Adapter { logger.error(`[INTERPAN MODE] Cannot execute non-InterPAN commands.`, NS); // will be caught by request queue and rejected internally. - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); + throw new EzspError(EzspStatus.ERROR_INVALID_CALL); } } diff --git a/src/adapter/ember/adapter/oneWaitress.ts b/src/adapter/ember/adapter/oneWaitress.ts index 632230c100..13b799f20b 100644 --- a/src/adapter/ember/adapter/oneWaitress.ts +++ b/src/adapter/ember/adapter/oneWaitress.ts @@ -1,8 +1,9 @@ /* istanbul ignore file */ import equals from 'fast-deep-equal/es6'; import {ZclPayload} from '../../events'; +import {NodeId} from '../../../zspec/tstypes'; import {TOUCHLINK_PROFILE_ID} from '../../../zspec/consts'; -import {EmberApsFrame, EmberNodeId} from '../types'; +import {EmberApsFrame} from '../types'; import {EmberZdoStatus} from '../zdo'; import {logger} from '../../../utils/logger'; @@ -22,7 +23,7 @@ type OneWaitressMatcher = { * Matches `indexOrDestination` in `ezspMessageSentHandler` or `sender` in `ezspIncomingMessageHandler` * Except for InterPAN touchlink, it should always be present. */ - target?: EmberNodeId, + target?: NodeId, apsFrame: EmberApsFrame, /** Cluster ID for when the response doesn't match the request. Takes priority over apsFrame.clusterId. Should be mostly for ZDO requests. */ responseClusterId?: number, @@ -110,7 +111,7 @@ export class EmberOneWaitress { * @param payload * @returns */ - public resolveZDO(status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: unknown): boolean { + public resolveZDO(status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: unknown): boolean { for (const [index, waiter] of this.waiters.entries()) { if (waiter.timedout) { this.waiters.delete(index); diff --git a/src/adapter/ember/adapter/requestQueue.ts b/src/adapter/ember/adapter/requestQueue.ts index 2a97196ffa..8d6236c8a2 100644 --- a/src/adapter/ember/adapter/requestQueue.ts +++ b/src/adapter/ember/adapter/requestQueue.ts @@ -1,6 +1,7 @@ /* istanbul ignore file */ import {logger} from "../../../utils/logger"; -import {EmberStatus, EzspStatus} from "../enums"; +import {EzspStatus, SLStatus} from "../enums"; +import {EzspError} from "../ezspError"; const NS = 'zh:ember:queue'; @@ -11,12 +12,12 @@ interface EmberRequestQueueEntry { */ sendAttempts: number; /** The function the entry is supposed to execute. */ - func: () => Promise; + func: () => Promise; /** The wrapping promise's reject to reject if necessary. */ reject: (reason: Error) => void; }; -export const NETWORK_BUSY_DEFER_MSEC = 500; +export const BUSY_DEFER_MSEC = 500; export const NETWORK_DOWN_DEFER_MSEC = 1500; export class EmberRequestQueue { @@ -76,7 +77,7 @@ export class EmberRequestQueue { * @param prioritize If true, function will be enqueued in the priority queue. Defaults to false. * @returns new length of the queue. */ - public enqueue(func: () => Promise, reject: (reason: Error) => void, prioritize: boolean = false): number { + public enqueue(func: () => Promise, reject: (reason: Error) => void, prioritize: boolean = false): number { logger.debug(`Status queue=${this.queue.length} priorityQueue=${this.priorityQueue.length}.`, NS); return (prioritize ? this.priorityQueue : this.queue).push({ sendAttempts: 0, @@ -88,7 +89,7 @@ export class EmberRequestQueue { /** * Dispatch the head of the queue. * - * If request `func` throws, catch error and reject the request. `ezsp${x}` functions throw `EzspStatus` as error. + * If request `func` throws, catch error and reject the request. `ezsp${x}` functions throw `EzspError`. * * If request `func` resolves but has an error, look at what error, and determine if should retry or remove the request from queue. * @@ -114,27 +115,28 @@ export class EmberRequestQueue { // NOTE: refer to `enqueue()` comment to keep logic in sync with expectations, adjust comment on change. try { - const status: EmberStatus = (await entry.func()); + const status: SLStatus = (await entry.func()); - if ((status === EmberStatus.MAX_MESSAGE_LIMIT_REACHED) || (status === EmberStatus.NETWORK_BUSY)) { + // XXX: add NOT_READY? + if ((status === SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED) || (status === SLStatus.BUSY)) { logger.debug(`Dispatching deferred: NCP busy.`, NS); - this.defer(NETWORK_BUSY_DEFER_MSEC); - } else if (status === EmberStatus.NETWORK_DOWN) { + this.defer(BUSY_DEFER_MSEC); + } else if (status === SLStatus.NETWORK_DOWN) { logger.debug(`Dispatching deferred: Network not ready`, NS); this.defer(NETWORK_DOWN_DEFER_MSEC); } else { // success (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); - if (status !== EmberStatus.SUCCESS) { - entry.reject(new Error(EmberStatus[status])); + if (status !== SLStatus.OK) { + entry.reject(new Error(SLStatus[status])); } } - } catch (err) {// message is EzspStatus string from ezsp${x} commands, except for stuff rejected by OneWaitress, but that's never "retry" - if (err.message === EzspStatus[EzspStatus.NO_TX_SPACE]) { + } catch (err) {// EzspStatusError from ezsp${x} commands, except for stuff rejected by OneWaitress, but that's never "retry" + if ((err as EzspError).code === EzspStatus.NO_TX_SPACE) { logger.debug(`Dispatching deferred: Host busy.`, NS); - this.defer(NETWORK_BUSY_DEFER_MSEC); - } else if (err.message === EzspStatus[EzspStatus.NOT_CONNECTED]) { + this.defer(BUSY_DEFER_MSEC); + } else if ((err as EzspError).code === EzspStatus.NOT_CONNECTED) { logger.debug(`Dispatching deferred: Network not ready`, NS); this.defer(NETWORK_DOWN_DEFER_MSEC); } else { diff --git a/src/adapter/ember/adapter/tokensManager.ts b/src/adapter/ember/adapter/tokensManager.ts index 9f4bfd0a7d..fccb650f3d 100644 --- a/src/adapter/ember/adapter/tokensManager.ts +++ b/src/adapter/ember/adapter/tokensManager.ts @@ -2,7 +2,7 @@ import {initSecurityManagerContext} from "../utils/initters"; import {BLANK_EUI64} from "../../../zspec"; import {EMBER_ENCRYPTION_KEY_SIZE, EUI64_SIZE} from "../ezsp/consts"; -import {EmberStatus, EzspStatus, SLStatus, SecManFlag, SecManKeyType} from "../enums"; +import {SLStatus, SecManFlag, SecManKeyType} from "../enums"; import {EzspValueId} from "../ezsp/enums"; import {EmberTokenData, SecManKey} from "../types"; import {Ezsp} from "../ezsp/ezsp"; @@ -303,8 +303,8 @@ export class EmberTokensManager { private static async ncpUsesPSAKeyStorage(ezsp: Ezsp): Promise { const [status, valueLength, value] = (await ezsp.ezspGetValue(EzspValueId.KEY_STORAGE_VERSION, 1)); - if ((status !== EzspStatus.SUCCESS) || (valueLength < 1)) { - throw new Error(`[TOKENS] Error retrieving key storage version, status=${EzspStatus[status]}.`); + if ((status !== SLStatus.OK) || (valueLength < 1)) { + throw new Error(`[TOKENS] Error retrieving key storage version, status=${SLStatus[status]}.`); } return (value[0] === 1); @@ -356,7 +356,7 @@ export class EmberTokensManager { const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); let writeOffset: number = 0; - if (tiStatus === EmberStatus.SUCCESS) { + if (tiStatus === SLStatus.OK) { const outputToken = Buffer.alloc(4 + 1 + 1 + (tokenInfo.size * tokenInfo.arraySize)); outputToken.writeUInt32LE(tokenInfo.nvm3Key, writeOffset);// 4 bytes writeOffset += 4; @@ -366,7 +366,7 @@ export class EmberTokensManager { for (let arrayIndex = 0; arrayIndex < tokenInfo.arraySize; arrayIndex++) { const [tdStatus, tokenData] = (await ezsp.ezspGetTokenData(tokenInfo.nvm3Key, arrayIndex)); - if (tdStatus === EmberStatus.SUCCESS) { + if (tdStatus === SLStatus.OK) { if (hasSecureStorage) { // Populate keys into tokenData because tokens do not contain them with secure key storage await EmberTokensManager.saveKeysToData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex); @@ -396,13 +396,13 @@ export class EmberTokensManager { outputToken.set(tokenData.data, writeOffset); writeOffset += tokenData.size; } else { - logger.error(`[TOKENS] Failed to get token data at index ${arrayIndex} with status=${EmberStatus[tdStatus]}.`, NS); + logger.error(`[TOKENS] Failed to get token data at index ${arrayIndex} with status=${SLStatus[tdStatus]}.`, NS); } } chunks.push(outputToken); } else { - logger.error(`[TOKENS] Failed to get token info at index ${i} with status=${EmberStatus[tiStatus]}.`, NS); + logger.error(`[TOKENS] Failed to get token info at index ${i} with status=${SLStatus[tiStatus]}.`, NS); } } @@ -423,9 +423,9 @@ export class EmberTokensManager { * * @see EmberTokensManager.saveTokens() for format * - * @return EmberStatus status code + * @return SLStatus status code */ - public static async restoreTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { + public static async restoreTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { if (!inBuffer?.length) { throw new Error(`[TOKENS] Restore tokens buffer empty.`); } @@ -441,7 +441,7 @@ export class EmberTokensManager { for (let i = 0; i < inTokenCount; i++) { const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); - if (tiStatus === EmberStatus.SUCCESS) { + if (tiStatus === SLStatus.OK) { const nvm3Key = inBuffer.readUInt32LE(readOffset);// 4 bytes Token Key/Creator readOffset += 4; const size = inBuffer.readUInt8(readOffset++);// 1 byte token size @@ -458,20 +458,20 @@ export class EmberTokensManager { await EmberTokensManager.restoreKeysFromData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex); } - const status = (await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData)) as EmberStatus; + const status = (await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData)); - if (status !== EmberStatus.SUCCESS) { - logger.error(`[TOKENS] Failed to set token data for key "${nvm3Key}" with status=${EmberStatus[status]}.`, NS); + if (status !== SLStatus.OK) { + logger.error(`[TOKENS] Failed to set token data for key "${nvm3Key}" with status=${SLStatus[status]}.`, NS); } readOffset += tokenData.size; } } else { - logger.error(`[TOKENS] Failed to get token info at index ${i} with status=${EmberStatus[tiStatus]}.`, NS); + logger.error(`[TOKENS] Failed to get token info at index ${i} with status=${SLStatus[tiStatus]}.`, NS); } } - return EmberStatus.SUCCESS; + return SLStatus.OK; } /** @@ -496,7 +496,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 0; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 0);// at beginning } else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) { @@ -508,7 +508,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 1; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 0);// at beginning } else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) { @@ -520,7 +520,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.TC_LINK; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 2 + EUI64_SIZE);// uint16_t+uint8_t[8] } else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) { @@ -531,7 +531,7 @@ export class EmberTokensManager { //this must be set to export a specific link key from table context.flags |= SecManFlag.KEY_INDEX_IS_VALID; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, KEY_ENTRY_KEY_DATA_OFFSET);// end part of uint8_t[25] } else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) { @@ -553,7 +553,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY; context.keyIndex = index; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 1 + 4 + 8 + 1 + 1);// uint8_t+uint32_t+uint8_t[8]+uint8_t+uint8_t } else if (nvm3Key === NVM3KEY_STACK_GP_SINK_TABLE) { @@ -576,7 +576,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.GREEN_POWER_SINK_TABLE_KEY; context.keyIndex = index; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 1 + 2 + 8 + 1 + 1);// uint8_t+uint16_t+uint8_t[8]+uint8_t+uint8_t } else if (nvm3Key === NVM3KEY_STACK_ZLL_SECURITY) { @@ -589,13 +589,13 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.ZLL_ENCRYPTION_KEY; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 4 + 1);// uint32_t+uint8_t context.coreKeyType = SecManKeyType.ZLL_PRECONFIGURED_KEY; - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = (await ezsp.ezspExportKey(context)); tokenData.data.set(plaintextKey.contents, 4 + 1 + EMBER_ENCRYPTION_KEY_SIZE);// uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE] } else { @@ -740,9 +740,9 @@ export class EmberTokensManager { /** * Updates zigbeed tokens from a backup of NCP tokens. * - * @return EmberStatus status code + * @return SLStatus status code */ - public static async writeNcpTokensToZigbeedTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { + public static async writeNcpTokensToZigbeedTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { if (!inBuffer?.length) { throw new Error(`[TOKENS] Restore tokens buffer empty.`); } @@ -767,9 +767,9 @@ export class EmberTokensManager { const creator = EmberTokensManager.getCreatorFromNvm3Key(nvm3Key);// uint16_t const status = (await ezsp.ezspSetTokenData(creator, arrayIndex, tokenData)); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { logger.error( - `[TOKENS] Failed to set Zigbeed token data for key "${nvm3Key}" creator "${creator}" with status=${EmberStatus[status]}.`, + `[TOKENS] Failed to set Zigbeed token data for key "${nvm3Key}" creator "${creator}" with status=${SLStatus[status]}.`, NS, ); } @@ -778,6 +778,6 @@ export class EmberTokensManager { } } - return EmberStatus.SUCCESS; + return SLStatus.OK; } } diff --git a/src/adapter/ember/consts.ts b/src/adapter/ember/consts.ts index 6dac7193ba..7f07a113d4 100644 --- a/src/adapter/ember/consts.ts +++ b/src/adapter/ember/consts.ts @@ -224,3 +224,5 @@ export const INTERPAN_APS_FRAME_CONTROL_NO_DELIVERY_MODE = (INTERPAN_APS_FRAME_T export const INTERPAN_APS_FRAME_DELIVERY_MODE_MASK = 0x0C; export const INTERPAN_APS_FRAME_SECURITY = 0x20; + +export const MANUFACTURING_STRING_SIZE = 16; diff --git a/src/adapter/ember/enums.ts b/src/adapter/ember/enums.ts index caeb735b98..76a51d106c 100644 --- a/src/adapter/ember/enums.ts +++ b/src/adapter/ember/enums.ts @@ -1,5 +1,5 @@ -/** Status Defines */ +/** Status Codes contains error and status code definitions used by Simplicity SDK software components and stacks. */ export enum SLStatus { // ----------------------------------------------------------------------------- // Generic Errors @@ -215,6 +215,42 @@ export enum SLStatus { /** Bad scan duration. */ BAD_SCAN_DURATION = 0x0050, + // ----------------------------------------------------------------------------- + // MAC transmit related status + + /** The MAC transmit queue is full */ + MAC_TRANSMIT_QUEUE_FULL = 0x0053, + /** + * The transmit attempt failed because the radio scheduler could not find a slot to transmit this packet in or + * a higher priority event interrupted it + */ + TRANSMIT_SCHEDULER_FAIL = 0x0054, + /** An unsupported channel setting was specified */ + TRANSMIT_INVALID_CHANNEL = 0x0055, + /** An unsupported power setting was specified */ + TRANSMIT_INVALID_POWER = 0x0056, + /** The expected ACK was received after the last transmission */ + TRANSMIT_ACK_RECEIVED = 0x0057, + /** + * The transmit attempt was blocked from going over the air. + * Typically this is due to the Radio Hold Off (RHO) or Coexistence plugins as they can prevent transmits based on external signals. + */ + TRANSMIT_BLOCKED = 0x0058, + + // ----------------------------------------------------------------------------- + // NVM3 specific errors + + /** The initialization was aborted as the NVM3 instance is not aligned properly in memory */ + NVM3_ALIGNMENT_INVALID = 0x0059, + /** The initialization was aborted as the size of the NVM3 instance is too small */ + NVM3_SIZE_TOO_SMALL = 0x005A, + /** The initialization was aborted as the NVM3 page size is not supported */ + NVM3_PAGE_SIZE_NOT_SUPPORTED = 0x005B, + /** The application that there was an error initializing some of the tokens */ + NVM3_TOKEN_INIT_FAILED = 0x005C, + /** The initialization was aborted as the NVM3 instance was already opened with other parameters */ + NVM3_OPENED_WITH_OTHER_PARAMETERS = 0x005D, + // ----------------------------------------------------------------------------- // Bluetooth status codes @@ -640,454 +676,528 @@ export enum SLStatus { COMPUTE_MATH_OVERFLOW = 0x1514, /** MATH numeric underflow */ COMPUTE_MATH_UNDERFLOW = 0x1515, -}; - -/** - * Many EmberZNet API functions return an ::EmberStatus value to indicate the success or failure of the call. - * Return codes are one byte long. - */ -export enum EmberStatus { - // Generic Messages. These messages are system wide. - /** The generic "no error" message. */ - SUCCESS = 0x00, - /** The generic "fatal error" message. */ - ERR_FATAL = 0x01, - /** An invalid value was passed as an argument to a function. */ - BAD_ARGUMENT = 0x02, - /** The requested information was not found. */ - NOT_FOUND = 0x03, - /** The manufacturing and stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ - EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04, - /** The manufacturing token format in non-volatile memory is different than what the stack expects (returned at initialization). */ - EEPROM_MFG_VERSION_MISMATCH = 0x06, - /** The stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ - EEPROM_STACK_VERSION_MISMATCH = 0x07, - - // Packet Buffer Module Errors - /** There are no more buffers. */ - NO_BUFFERS = 0x18, - /** Packet is dropped by packet-handoff callbacks. */ - PACKET_HANDOFF_DROP_PACKET = 0x19, - - // Serial Manager Errors - /** Specifies an invalid baud rate. */ - SERIAL_INVALID_BAUD_RATE = 0x20, - /** Specifies an invalid serial port. */ - SERIAL_INVALID_PORT = 0x21, - /** Tried to send too much data. */ - SERIAL_TX_OVERFLOW = 0x22, - /** There wasn't enough space to store a received character and the character was dropped. */ - SERIAL_RX_OVERFLOW = 0x23, - /** Detected a UART framing error. */ - SERIAL_RX_FRAME_ERROR = 0x24, - /** Detected a UART parity error. */ - SERIAL_RX_PARITY_ERROR = 0x25, - /** There is no received data to process. */ - SERIAL_RX_EMPTY = 0x26, - /** The receive interrupt was not handled in time and a character was dropped. */ - SERIAL_RX_OVERRUN_ERROR = 0x27, - - // MAC Errors - /** The MAC transmit queue is full. */ - MAC_TRANSMIT_QUEUE_FULL = 0x39, - // Internal - /** MAC header FCF error on receive. */ - MAC_UNKNOWN_HEADER_TYPE = 0x3A, - /** MAC ACK header received. */ - MAC_ACK_HEADER_TYPE = 0x3B, - /** The MAC can't complete this task because it is scanning. */ - MAC_SCANNING = 0x3D, - /** No pending data exists for a data poll. */ - MAC_NO_DATA = 0x31, - /** Attempts to scan when joined to a network. */ - MAC_JOINED_NETWORK = 0x32, - /** Scan duration must be 0 to 14 inclusive. Tried to scan with an incorrect duration value. */ - MAC_BAD_SCAN_DURATION = 0x33, - /** emberStartScan was called with an incorrect scan type. */ - MAC_INCORRECT_SCAN_TYPE = 0x34, - /** emberStartScan was called with an invalid channel mask. */ - MAC_INVALID_CHANNEL_MASK = 0x35, - /** Failed to scan the current channel because the relevant MAC command could not be transmitted. */ - MAC_COMMAND_TRANSMIT_FAILURE = 0x36, - /** An ACK was expected following the transmission but the MAC level ACK was never received. */ - MAC_NO_ACK_RECEIVED = 0x40, - /** MAC failed to transmit a message because it could not successfully perform a radio network switch. */ - MAC_RADIO_NETWORK_SWITCH_FAILED = 0x41, - /** An indirect data message timed out before a poll requested it. */ - MAC_INDIRECT_TIMEOUT = 0x42, - - // Simulated EEPROM Errors - /** - * The Simulated EEPROM is telling the application that at least one flash page to be erased. - * The GREEN status means the current page has not filled above the ::ERASE_CRITICAL_THRESHOLD. - * - * The application should call the function ::halSimEepromErasePage() when it can to erase a page. - */ - SIM_EEPROM_ERASE_PAGE_GREEN = 0x43, - /** - * The Simulated EEPROM is telling the application that at least one flash page must be erased. - * The RED status means the current page has filled above the ::ERASE_CRITICAL_THRESHOLD. - * - * Due to the shrinking availability of write space, data could be lost. - * The application must call the function ::halSimEepromErasePage() as soon as possible to erase a page. - */ - SIM_EEPROM_ERASE_PAGE_RED = 0x44, - /** - * The Simulated EEPROM has run out of room to write new data and the data trying to be set has been lost. - * This error code is the result of ignoring the ::SIM_EEPROM_ERASE_PAGE_RED error code. - * - * The application must call the function ::halSimEepromErasePage() to make room for any further calls to set a token. - */ - SIM_EEPROM_FULL = 0x45, - // Errors 46 and 47 are now defined below in the flash error block (was attempting to prevent renumbering). - /** - * Attempt 1 to initialize the Simulated EEPROM has failed. - * - * This failure means the information already stored in the Flash (or a lack thereof), - * is fatally incompatible with the token information compiled into the code image being run. - */ - SIM_EEPROM_INIT_1_FAILED = 0x48, - /** - * Attempt 2 to initialize the Simulated EEPROM has failed. - * - * This failure means Attempt 1 failed, and the token system failed to properly reload default tokens and reset the Simulated EEPROM. - */ - SIM_EEPROM_INIT_2_FAILED = 0x49, - /** - * Attempt 3 to initialize the Simulated EEPROM has failed. - * - * This failure means one or both of the tokens ::TOKEN_MFG_NVDATA_VERSION or ::TOKEN_STACK_NVDATA_VERSION - * were incorrect and the token system failed to properly reload default tokens and reset the Simulated EEPROM. - */ - SIM_EEPROM_INIT_3_FAILED = 0x4A, - /** - * The Simulated EEPROM is repairing itself. - * - * While there's nothing for an app to do when the SimEE is going to - * repair itself (SimEE has to be fully functional for the rest of the - * system to work), alert the application to the fact that repair - * is occurring. There are debugging scenarios where an app might want - * to know that repair is happening, such as monitoring frequency. - * @note Common situations will trigger an expected repair, such as - * using an erased chip or changing token definitions. - */ - SIM_EEPROM_REPAIRING = 0x4D, - - // Flash Errors - /** - * A fatal error has occurred while trying to write data to the Flash. - * The target memory attempting to be programmed is already programmed. - * The flash write routines were asked to flip a bit from a 0 to 1, - * which is physically impossible and the write was therefore inhibited. - * The data in the Flash cannot be trusted after this error. - */ - ERR_FLASH_WRITE_INHIBITED = 0x46, - /** - * A fatal error has occurred while trying to write data to the Flash and the write verification has failed. - * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. - */ - ERR_FLASH_VERIFY_FAILED = 0x47, - /** - * A fatal error has occurred while trying to write data to the Flash possibly due to write protection or an invalid address. - * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. - */ - ERR_FLASH_PROG_FAIL = 0x4B, - /** - * A fatal error has occurred while trying to erase the Flash possibly due to write protection. - * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. - */ - ERR_FLASH_ERASE_FAIL = 0x4C, - - // Bootloader Errors - /** The bootloader received an invalid message (failed attempt to go into bootloader). */ - ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58, - /** The bootloader received an invalid message (failed attempt to go into the bootloader). */ - ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59, - /** The bootloader cannot complete the bootload operation because either an image was not found or the image exceeded memory bounds. */ - ERR_BOOTLOADER_NO_IMAGE = 0x05A, - - // Transport Errors - /** The APS layer attempted to send or deliver a message and failed. */ - DELIVERY_FAILED = 0x66, - /** This binding index is out of range for the current binding table. */ - BINDING_INDEX_OUT_OF_RANGE = 0x69, - /** This address table index is out of range for the current address table. */ - ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A, - /** An invalid binding table index was given to a function. */ - INVALID_BINDING_INDEX = 0x6C, - /** The API call is not allowed given the current state of the stack. */ - INVALID_CALL = 0x70, - /** The link cost to a node is not known. */ - COST_NOT_KNOWN = 0x71, - /** The maximum number of in-flight messages = i.e., ::EMBER_APS_UNICAST_MESSAGE_COUNT, has been reached. */ - MAX_MESSAGE_LIMIT_REACHED = 0x72, - /** The message to be transmitted is too big to fit into a single over-the-air packet. */ - MESSAGE_TOO_LONG = 0x74, - /** The application is trying to delete or overwrite a binding that is in use. */ - BINDING_IS_ACTIVE = 0x75, - /** The application is trying to overwrite an address table entry that is in use. */ - ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76, - /** An attempt was made to transmit during the suspend period. */ - TRANSMISSION_SUSPENDED = 0x77, - - // Green Power status codes - /** Security match. */ - MATCH = 0x78, - /** Drop frame. */ - DROP_FRAME = 0x79, - /** */ - PASS_UNPROCESSED = 0x7A, - /** */ - TX_THEN_DROP = 0x7B, - /** */ - NO_SECURITY = 0x7C, - /** */ - COUNTER_FAILURE = 0x7D, - /** */ - AUTH_FAILURE = 0x7E, - /** */ - UNPROCESSED = 0x7F, - - // HAL Module Errors - /** The conversion is complete. */ - ADC_CONVERSION_DONE = 0x80, - /** The conversion cannot be done because a request is being processed. */ - ADC_CONVERSION_BUSY = 0x81, - /** The conversion is deferred until the current request has been processed. */ - ADC_CONVERSION_DEFERRED = 0x82, - /** No results are pending. */ - ADC_NO_CONVERSION_PENDING = 0x84, - /** Sleeping (for a duration) has been abnormally interrupted and exited prematurely. */ - SLEEP_INTERRUPTED = 0x85, - - // PHY Errors - /** - * The transmit attempt failed because the radio scheduler could not find a slot - * to transmit this packet in or a higher priority event interrupted it. - */ - PHY_TX_SCHED_FAIL = 0x87, - /** The transmit hardware buffer underflowed. */ - PHY_TX_UNDERFLOW = 0x88, - /** The transmit hardware did not finish transmitting a packet. */ - PHY_TX_INCOMPLETE = 0x89, - /** An unsupported channel setting was specified. */ - PHY_INVALID_CHANNEL = 0x8A, - /** An unsupported power setting was specified. */ - PHY_INVALID_POWER = 0x8B, - /** The requested operation cannot be completed because the radio is currently busy, either transmitting a packet or performing calibration. */ - PHY_TX_BUSY = 0x8C, - /** The transmit attempt failed because all CCA attempts indicated that the channel was busy. */ - PHY_TX_CCA_FAIL = 0x8D, + + // ----------------------------------------------------------------------------- + // Zigbee status codes + + /** Packet is dropped by packet-handoff callbacks */ + ZIGBEE_PACKET_HANDOFF_DROPPED = 0x0C01, + /** The APS layer attempted to send or deliver a message and failed */ + ZIGBEE_DELIVERY_FAILED = 0x0C02, + /** The maximum number of in-flight messages ::EMBER_APS_UNICAST_MESSAGE_COUNT has been reached */ + ZIGBEE_MAX_MESSAGE_LIMIT_REACHED = 0x0C03, + /** The application is trying to delete or overwrite a binding that is in use */ + ZIGBEE_BINDING_IS_ACTIVE = 0x0C04, + /** The application is trying to overwrite an address table entry that is in use */ + ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x0C05, + /** After moving, a mobile node's attempt to re-establish contact with the network failed */ + ZIGBEE_MOVE_FAILED = 0x0C06, + /** The local node ID has changed. The application can get the new node ID by calling ::sl_zigbee_get_node_id() */ + ZIGBEE_NODE_ID_CHANGED = 0x0C07, + /** The chosen security level is not supported by the stack */ + ZIGBEE_INVALID_SECURITY_LEVEL = 0x0C08, + /** An error occurred when trying to encrypt at the APS Level */ + ZIGBEE_IEEE_ADDRESS_DISCOVERY_IN_PROGRESS = 0x0C09, + /** An error occurred when trying to encrypt at the APS Level */ + ZIGBEE_APS_ENCRYPTION_ERROR = 0x0C0A, + /** There was an attempt to form or join a network with security without calling ::sl_zigbee_set_initial_security_state() first */ + ZIGBEE_SECURITY_STATE_NOT_SET = 0x0C0B, /** - * The transmit attempt was blocked from going over the air. - * Typically this is due to the Radio Hold Off (RHO) or Coexistence plugins as they can prevent transmits based on external signals. - */ - PHY_TX_BLOCKED = 0x8E, - /** The expected ACK was received after the last transmission. */ - PHY_ACK_RECEIVED = 0x8F, - - // Return Codes Passed to emberStackStatusHandler() See also ::emberStackStatusHandler = ,. - /** The stack software has completed initialization and is ready to send and receive packets over the air. */ - NETWORK_UP = 0x90, - /** The network is not operating. */ - NETWORK_DOWN = 0x91, - /** An attempt to join a network failed. */ - JOIN_FAILED = 0x94, - /** After moving, a mobile node's attempt to re-establish contact with the network failed. */ - MOVE_FAILED = 0x96, - /** - * An attempt to join as a router failed due to a Zigbee versus Zigbee Pro incompatibility. - * Zigbee devices joining Zigbee Pro networks (or vice versa) must join as End Devices, not Routers. - */ - CANNOT_JOIN_AS_ROUTER = 0x98, - /** The local node ID has changed. The application can get the new node ID by calling ::emberGetNodeId(). */ - NODE_ID_CHANGED = 0x99, - /** The local PAN ID has changed. The application can get the new PAN ID by calling ::emberGetPanId(). */ - PAN_ID_CHANGED = 0x9A, + * There was an attempt to broadcast a key switch too quickly after broadcasting the next network key. + * The Trust Center must wait at least a period equal to the broadcast timeout so that all routers have a chance + * to receive the broadcast of the new network key + */ + ZIGBEE_TOO_SOON_FOR_SWITCH_KEY = 0x0C0C, + /** The received signature corresponding to the message that was passed to the CBKE Library failed verification and is not valid */ + ZIGBEE_SIGNATURE_VERIFY_FAILURE = 0x0C0D, + /** The message could not be sent because the link key corresponding to the destination is not authorized for use in APS data messages */ + ZIGBEE_KEY_NOT_AUTHORIZED = 0x0C0E, + /** The application tried to use a binding that has been remotely modified and the change has not yet been reported to the application */ + ZIGBEE_BINDING_HAS_CHANGED = 0x0C0F, + /** The EUI of the Trust center has changed due to a successful rejoin after TC Swapout */ + ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED = 0x0C10, + /** A Trust Center Swapout Rejoin has occurred without the EUI of the TC changing */ + ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_NOT_CHANGED = 0x0C11, + /** An attempt to generate random bytes failed because of insufficient random data from the radio */ + ZIGBEE_INSUFFICIENT_RANDOM_DATA = 0x0C12, + /** A Zigbee route error command frame was received indicating that a source routed message from this node failed en route */ + ZIGBEE_SOURCE_ROUTE_FAILURE = 0x0C13, + /** A Zigbee route error command frame was received indicating that a message sent to this node along a many-to-one route failed en route */ + ZIGBEE_MANY_TO_ONE_ROUTE_FAILURE = 0x0C14, + /** A critical and fatal error indicating that the version of the stack trying to run does not match with the chip it's running on */ + ZIGBEE_STACK_AND_HARDWARE_MISMATCH = 0x0C15, + /** The local PAN ID has changed. The application can get the new PAN ID by calling ::emberGetPanId() */ + ZIGBEE_PAN_ID_CHANGED = 0x0C16, /** The channel has changed. */ - CHANNEL_CHANGED = 0x9B, + ZIGBEE_CHANNEL_CHANGED = 0x0C17, /** The network has been opened for joining. */ - NETWORK_OPENED = 0x9C, + ZIGBEE_NETWORK_OPENED = 0x0C18, /** The network has been closed for joining. */ - NETWORK_CLOSED = 0x9D, - /** An attempt to join or rejoin the network failed because no router beacons could be heard by the joining node. */ - NO_BEACONS = 0xAB, + ZIGBEE_NETWORK_CLOSED = 0x0C19, /** * An attempt was made to join a Secured Network using a pre-configured key, but the Trust Center sent back a - * Network Key in-the-clear when an encrypted Network Key was required. (::EMBER_REQUIRE_ENCRYPTED_KEY). + * Network Key in-the-clear when an encrypted Network Key was required. (::EMBER_REQUIRE_ENCRYPTED_KEY) */ - RECEIVED_KEY_IN_THE_CLEAR = 0xAC, + ZIGBEE_RECEIVED_KEY_IN_THE_CLEAR = 0x0C1A, /** An attempt was made to join a Secured Network, but the device did not receive a Network Key. */ - NO_NETWORK_KEY_RECEIVED = 0xAD, + ZIGBEE_NO_NETWORK_KEY_RECEIVED = 0x0C1B, /** After a device joined a Secured Network, a Link Key was requested (::EMBER_GET_LINK_KEY_WHEN_JOINING) but no response was ever received. */ - NO_LINK_KEY_RECEIVED = 0xAE, - /** - * An attempt was made to join a Secured Network without a pre-configured key, - * but the Trust Center sent encrypted data using a pre-configured key. - */ - PRECONFIGURED_KEY_REQUIRED = 0xAF, - - // Security Errors - /** The passed key data is not valid. A key of all zeros or all F's are reserved values and cannot be used. */ - KEY_INVALID = 0xB2, - /** The chosen security level (the value of ::EMBER_SECURITY_LEVEL) is not supported by the stack. */ - INVALID_SECURITY_LEVEL = 0x95, - /** - * An error occurred when trying to encrypt at the APS Level. - * - * To APS encrypt an outgoing packet, the sender - * needs to know the EUI64 of the destination. This error occurs because - * the EUI64 of the destination can't be determined from - * the short address (no entry in the neighbor, child, binding - * or address tables). - * - * Every time this error code is seen, note that the stack initiates an - * IEEE address discovery request behind the scenes. Responses - * to the request are stored in the trust center cache portion of the - * address table. Note that you need at least 1 entry allocated for - * TC cache in the address table plugin. Depending on the available rows in - * the table, newly discovered addresses may replace old ones. The address - * table plugin is enabled by default on the host. If you are using an SoC - * platform, please be sure to add the address table plugin. - * - * When customers choose to send APS messages by using short addresses, - * they should incorporate a retry mechanism and try again, no sooner than - * 2 seconds later, to resend the APS message. If the app always - * receives 0xBE (IEEE_ADDRESS_DISCOVERY_IN_PROGRESS) after - * multiple retries, that might indicate that: - * a) destination node is not on the network - * b) there are problems with the health of the network - * c) there may not be any space set aside in the address table for - * the newly discovered address - this can be rectified by reserving - * more entries for the trust center cache in the address table plugin - */ - IEEE_ADDRESS_DISCOVERY_IN_PROGRESS = 0xBE, - /** - * An error occurred when trying to encrypt at the APS Level. - * - * This error occurs either because the long address of the recipient can't be - * determined from the short address (no entry in the binding table) - * or there is no link key entry in the table associated with the destination, - * or there was a failure to load the correct key into the encryption core. - */ - APS_ENCRYPTION_ERROR = 0xA6, - /** There was an attempt to form or join a network with security without calling ::emberSetInitialSecurityState() first. */ - SECURITY_STATE_NOT_SET = 0xA8, + ZIGBEE_NO_LINK_KEY_RECEIVED = 0x0C1C, /** - * There was an attempt to set an entry in the key table using an invalid long address. Invalid addresses include: - * - The local device's IEEE address - * - Trust Center's IEEE address - * - An existing table entry's IEEE address - * - An address consisting of all zeros or all F's + * An attempt was made to join a Secured Network without a pre-configured key, but the Trust Center sent encrypted data using a + * pre-configured key. */ - KEY_TABLE_INVALID_ADDRESS = 0xB3, - /** There was an attempt to set a security configuration that is not valid given the other security settings. */ - SECURITY_CONFIGURATION_INVALID = 0xB7, - /** - * There was an attempt to broadcast a key switch too quickly after broadcasting the next network key. - * The Trust Center must wait at least a period equal to the broadcast timeout so that all routers have a chance - * to receive the broadcast of the new network key. - */ - TOO_SOON_FOR_SWITCH_KEY = 0xB8, - /** The received signature corresponding to the message that was passed to the CBKE Library failed verification and is not valid. */ - SIGNATURE_VERIFY_FAILURE = 0xB9, - /** - * The message could not be sent because the link key corresponding to the destination is not authorized for use in APS data messages. - * APS Commands (sent by the stack) are allowed. - * To use it for encryption of APS data messages it must be authorized using a key agreement protocol (such as CBKE). - */ - KEY_NOT_AUTHORIZED = 0xBB, - /** The security data provided was not valid, or an integrity check failed. */ - SECURITY_DATA_INVALID = 0xBD, - - // Miscellaneous Network Errors - /** The node has not joined a network. */ - NOT_JOINED = 0x93, - /** A message cannot be sent because the network is currently overloaded. */ - NETWORK_BUSY = 0xA1, - /** The application tried to send a message using an endpoint that it has not defined. */ - INVALID_ENDPOINT = 0xA3, - /** The application tried to use a binding that has been remotely modified and the change has not yet been reported to the application. */ - BINDING_HAS_CHANGED = 0xA4, - /** An attempt to generate random bytes failed because of insufficient random data from the radio. */ - INSUFFICIENT_RANDOM_DATA = 0xA5, - /** A Zigbee route error command frame was received indicating that a source routed message from this node failed en route. */ - SOURCE_ROUTE_FAILURE = 0xA9, - /** - * A Zigbee route error command frame was received indicating that a message sent to this node along a many-to-one route failed en route. - * The route error frame was delivered by an ad-hoc search for a functioning route. - */ - MANY_TO_ONE_ROUTE_FAILURE = 0xAA, - - // Miscellaneous Utility Errors - /** - * A critical and fatal error indicating that the version of the - * stack trying to run does not match with the chip it's running on. The - * software (stack) on the chip must be replaced with software - * compatible with the chip. - */ - STACK_AND_HARDWARE_MISMATCH = 0xB0, - /** An index was passed into the function that was larger than the valid range. */ - INDEX_OUT_OF_RANGE = 0xB1, - /** There are no empty entries left in the table. */ - TABLE_FULL = 0xB4, - /** The requested table entry has been erased and contains no valid data. */ - TABLE_ENTRY_ERASED = 0xB6, - /** The requested function cannot be executed because the library that contains the necessary functionality is not present. */ - LIBRARY_NOT_PRESENT = 0xB5, - /** The stack accepted the command and is currently processing the request. The results will be returned via an appropriate handler. */ - OPERATION_IN_PROGRESS = 0xBA, - /** - * The EUI of the Trust center has changed due to a successful rejoin. - * The device may need to perform other authentication to verify the new TC is authorized to take over. - */ - TRUST_CENTER_EUI_HAS_CHANGED = 0xBC, - /** Trust center swapped out. The EUI has changed. */ - TRUST_CENTER_SWAPPED_OUT_EUI_HAS_CHANGED = TRUST_CENTER_EUI_HAS_CHANGED, - /** Trust center swapped out. The EUI has not changed. */ - TRUST_CENTER_SWAPPED_OUT_EUI_HAS_NOT_CHANGED = 0xBF, - - // NVM3 Token Errors - /** NVM3 is telling the application that the initialization was aborted as no valid NVM3 page was found. */ - NVM3_TOKEN_NO_VALID_PAGES = 0xC0, - /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance was already opened with other parameters. */ - NVM3_ERR_OPENED_WITH_OTHER_PARAMETERS = 0xC1, - /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance is not aligned properly in memory. */ - NVM3_ERR_ALIGNMENT_INVALID = 0xC2, - /** NVM3 is telling the application that the initialization was aborted as the size of the NVM3 instance is too small. */ - NVM3_ERR_SIZE_TOO_SMALL = 0xC3, - /** NVM3 is telling the application that the initialization was aborted as the NVM3 page size is not supported. */ - NVM3_ERR_PAGE_SIZE_NOT_SUPPORTED = 0xC4, - /** NVM3 is telling the application that there was an error initializing some of the tokens. */ - NVM3_ERR_TOKEN_INIT = 0xC5, - /** NVM3 is telling the application there has been an error when attempting to upgrade SimEE tokens. */ - NVM3_ERR_UPGRADE = 0xC6, - /** NVM3 is telling the application that there has been an unknown error. */ - NVM3_ERR_UNKNOWN = 0xC7, - - // Application Errors. These error codes are available for application use. - /** - * This error is reserved for customer application use. - * This will never be returned from any portion of the network stack or HAL. - */ - APPLICATION_ERROR_0 = 0xF0, - APPLICATION_ERROR_1 = 0xF1, - APPLICATION_ERROR_2 = 0xF2, - APPLICATION_ERROR_3 = 0xF3, - APPLICATION_ERROR_4 = 0xF4, - APPLICATION_ERROR_5 = 0xF5, - APPLICATION_ERROR_6 = 0xF6, - APPLICATION_ERROR_7 = 0xF7, - APPLICATION_ERROR_8 = 0xF8, - APPLICATION_ERROR_9 = 0xF9, - APPLICATION_ERROR_10 = 0xFA, - APPLICATION_ERROR_11 = 0xFB, - APPLICATION_ERROR_12 = 0xFC, - APPLICATION_ERROR_13 = 0xFD, - APPLICATION_ERROR_14 = 0xFE, - APPLICATION_ERROR_15 = 0xFF, + ZIGBEE_PRECONFIGURED_KEY_REQUIRED = 0x0C1D, + /** A Zigbee EZSP error has occured. Track the origin and corresponding EzspStatus for more info. */ + ZIGBEE_EZSP_ERROR = 0x0C1E, }; +// /** +// * Many EmberZNet API functions return an ::EmberStatus value to indicate the success or failure of the call. +// * Return codes are one byte long. +// */ +// export enum EmberStatus { +// // Generic Messages. These messages are system wide. +// /** The generic "no error" message. */ +// SUCCESS = 0x00, +// /** The generic "fatal error" message. */ +// ERR_FATAL = 0x01, +// /** An invalid value was passed as an argument to a function. */ +// BAD_ARGUMENT = 0x02, +// /** The requested information was not found. */ +// NOT_FOUND = 0x03, +// /** The manufacturing and stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ +// EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04, +// /** The manufacturing token format in non-volatile memory is different than what the stack expects (returned at initialization). */ +// EEPROM_MFG_VERSION_MISMATCH = 0x06, +// /** The stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ +// EEPROM_STACK_VERSION_MISMATCH = 0x07, + +// // Packet Buffer Module Errors +// /** There are no more buffers. */ +// NO_BUFFERS = 0x18, +// /** Packet is dropped by packet-handoff callbacks. */ +// PACKET_HANDOFF_DROP_PACKET = 0x19, + +// // Serial Manager Errors +// /** Specifies an invalid baud rate. */ +// SERIAL_INVALID_BAUD_RATE = 0x20, +// /** Specifies an invalid serial port. */ +// SERIAL_INVALID_PORT = 0x21, +// /** Tried to send too much data. */ +// SERIAL_TX_OVERFLOW = 0x22, +// /** There wasn't enough space to store a received character and the character was dropped. */ +// SERIAL_RX_OVERFLOW = 0x23, +// /** Detected a UART framing error. */ +// SERIAL_RX_FRAME_ERROR = 0x24, +// /** Detected a UART parity error. */ +// SERIAL_RX_PARITY_ERROR = 0x25, +// /** There is no received data to process. */ +// SERIAL_RX_EMPTY = 0x26, +// /** The receive interrupt was not handled in time and a character was dropped. */ +// SERIAL_RX_OVERRUN_ERROR = 0x27, + +// // MAC Errors +// /** The MAC transmit queue is full. */ +// MAC_TRANSMIT_QUEUE_FULL = 0x39, +// // Internal +// /** MAC header FCF error on receive. */ +// MAC_UNKNOWN_HEADER_TYPE = 0x3A, +// /** MAC ACK header received. */ +// MAC_ACK_HEADER_TYPE = 0x3B, +// /** The MAC can't complete this task because it is scanning. */ +// MAC_SCANNING = 0x3D, +// /** No pending data exists for a data poll. */ +// MAC_NO_DATA = 0x31, +// /** Attempts to scan when joined to a network. */ +// MAC_JOINED_NETWORK = 0x32, +// /** Scan duration must be 0 to 14 inclusive. Tried to scan with an incorrect duration value. */ +// MAC_BAD_SCAN_DURATION = 0x33, +// /** emberStartScan was called with an incorrect scan type. */ +// MAC_INCORRECT_SCAN_TYPE = 0x34, +// /** emberStartScan was called with an invalid channel mask. */ +// MAC_INVALID_CHANNEL_MASK = 0x35, +// /** Failed to scan the current channel because the relevant MAC command could not be transmitted. */ +// MAC_COMMAND_TRANSMIT_FAILURE = 0x36, +// /** An ACK was expected following the transmission but the MAC level ACK was never received. */ +// MAC_NO_ACK_RECEIVED = 0x40, +// /** MAC failed to transmit a message because it could not successfully perform a radio network switch. */ +// MAC_RADIO_NETWORK_SWITCH_FAILED = 0x41, +// /** An indirect data message timed out before a poll requested it. */ +// MAC_INDIRECT_TIMEOUT = 0x42, + +// // Simulated EEPROM Errors +// /** +// * The Simulated EEPROM is telling the application that at least one flash page to be erased. +// * The GREEN status means the current page has not filled above the ::ERASE_CRITICAL_THRESHOLD. +// * +// * The application should call the function ::halSimEepromErasePage() when it can to erase a page. +// */ +// SIM_EEPROM_ERASE_PAGE_GREEN = 0x43, +// /** +// * The Simulated EEPROM is telling the application that at least one flash page must be erased. +// * The RED status means the current page has filled above the ::ERASE_CRITICAL_THRESHOLD. +// * +// * Due to the shrinking availability of write space, data could be lost. +// * The application must call the function ::halSimEepromErasePage() as soon as possible to erase a page. +// */ +// SIM_EEPROM_ERASE_PAGE_RED = 0x44, +// /** +// * The Simulated EEPROM has run out of room to write new data and the data trying to be set has been lost. +// * This error code is the result of ignoring the ::SIM_EEPROM_ERASE_PAGE_RED error code. +// * +// * The application must call the function ::halSimEepromErasePage() to make room for any further calls to set a token. +// */ +// SIM_EEPROM_FULL = 0x45, +// // Errors 46 and 47 are now defined below in the flash error block (was attempting to prevent renumbering). +// /** +// * Attempt 1 to initialize the Simulated EEPROM has failed. +// * +// * This failure means the information already stored in the Flash (or a lack thereof), +// * is fatally incompatible with the token information compiled into the code image being run. +// */ +// SIM_EEPROM_INIT_1_FAILED = 0x48, +// /** +// * Attempt 2 to initialize the Simulated EEPROM has failed. +// * +// * This failure means Attempt 1 failed, and the token system failed to properly reload default tokens and reset the Simulated EEPROM. +// */ +// SIM_EEPROM_INIT_2_FAILED = 0x49, +// /** +// * Attempt 3 to initialize the Simulated EEPROM has failed. +// * +// * This failure means one or both of the tokens ::TOKEN_MFG_NVDATA_VERSION or ::TOKEN_STACK_NVDATA_VERSION +// * were incorrect and the token system failed to properly reload default tokens and reset the Simulated EEPROM. +// */ +// SIM_EEPROM_INIT_3_FAILED = 0x4A, +// /** +// * The Simulated EEPROM is repairing itself. +// * +// * While there's nothing for an app to do when the SimEE is going to +// * repair itself (SimEE has to be fully functional for the rest of the +// * system to work), alert the application to the fact that repair +// * is occurring. There are debugging scenarios where an app might want +// * to know that repair is happening, such as monitoring frequency. +// * @note Common situations will trigger an expected repair, such as +// * using an erased chip or changing token definitions. +// */ +// SIM_EEPROM_REPAIRING = 0x4D, + +// // Flash Errors +// /** +// * A fatal error has occurred while trying to write data to the Flash. +// * The target memory attempting to be programmed is already programmed. +// * The flash write routines were asked to flip a bit from a 0 to 1, +// * which is physically impossible and the write was therefore inhibited. +// * The data in the Flash cannot be trusted after this error. +// */ +// ERR_FLASH_WRITE_INHIBITED = 0x46, +// /** +// * A fatal error has occurred while trying to write data to the Flash and the write verification has failed. +// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. +// */ +// ERR_FLASH_VERIFY_FAILED = 0x47, +// /** +// * A fatal error has occurred while trying to write data to the Flash possibly due to write protection or an invalid address. +// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. +// */ +// ERR_FLASH_PROG_FAIL = 0x4B, +// /** +// * A fatal error has occurred while trying to erase the Flash possibly due to write protection. +// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. +// */ +// ERR_FLASH_ERASE_FAIL = 0x4C, + +// // Bootloader Errors +// /** The bootloader received an invalid message (failed attempt to go into bootloader). */ +// ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58, +// /** The bootloader received an invalid message (failed attempt to go into the bootloader). */ +// ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59, +// /** The bootloader cannot complete the bootload operation because either an image was not found or the image exceeded memory bounds. */ +// ERR_BOOTLOADER_NO_IMAGE = 0x05A, + +// // Transport Errors +// /** The APS layer attempted to send or deliver a message and failed. */ +// DELIVERY_FAILED = 0x66, +// /** This binding index is out of range for the current binding table. */ +// BINDING_INDEX_OUT_OF_RANGE = 0x69, +// /** This address table index is out of range for the current address table. */ +// ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A, +// /** An invalid binding table index was given to a function. */ +// INVALID_BINDING_INDEX = 0x6C, +// /** The API call is not allowed given the current state of the stack. */ +// INVALID_CALL = 0x70, +// /** The link cost to a node is not known. */ +// COST_NOT_KNOWN = 0x71, +// /** The maximum number of in-flight messages = i.e., ::EMBER_APS_UNICAST_MESSAGE_COUNT, has been reached. */ +// MAX_MESSAGE_LIMIT_REACHED = 0x72, +// /** The message to be transmitted is too big to fit into a single over-the-air packet. */ +// MESSAGE_TOO_LONG = 0x74, +// /** The application is trying to delete or overwrite a binding that is in use. */ +// BINDING_IS_ACTIVE = 0x75, +// /** The application is trying to overwrite an address table entry that is in use. */ +// ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76, +// /** An attempt was made to transmit during the suspend period. */ +// TRANSMISSION_SUSPENDED = 0x77, + +// // Green Power status codes +// /** Security match. */ +// MATCH = 0x78, +// /** Drop frame. */ +// DROP_FRAME = 0x79, +// /** */ +// PASS_UNPROCESSED = 0x7A, +// /** */ +// TX_THEN_DROP = 0x7B, +// /** */ +// NO_SECURITY = 0x7C, +// /** */ +// COUNTER_FAILURE = 0x7D, +// /** */ +// AUTH_FAILURE = 0x7E, +// /** */ +// UNPROCESSED = 0x7F, + +// // HAL Module Errors +// /** The conversion is complete. */ +// ADC_CONVERSION_DONE = 0x80, +// /** The conversion cannot be done because a request is being processed. */ +// ADC_CONVERSION_BUSY = 0x81, +// /** The conversion is deferred until the current request has been processed. */ +// ADC_CONVERSION_DEFERRED = 0x82, +// /** No results are pending. */ +// ADC_NO_CONVERSION_PENDING = 0x84, +// /** Sleeping (for a duration) has been abnormally interrupted and exited prematurely. */ +// SLEEP_INTERRUPTED = 0x85, + +// // PHY Errors +// /** +// * The transmit attempt failed because the radio scheduler could not find a slot +// * to transmit this packet in or a higher priority event interrupted it. +// */ +// PHY_TX_SCHED_FAIL = 0x87, +// /** The transmit hardware buffer underflowed. */ +// PHY_TX_UNDERFLOW = 0x88, +// /** The transmit hardware did not finish transmitting a packet. */ +// PHY_TX_INCOMPLETE = 0x89, +// /** An unsupported channel setting was specified. */ +// PHY_INVALID_CHANNEL = 0x8A, +// /** An unsupported power setting was specified. */ +// PHY_INVALID_POWER = 0x8B, +// /** The requested operation cannot be completed because the radio is currently busy, either transmitting a packet or performing calibration. */ +// PHY_TX_BUSY = 0x8C, +// /** The transmit attempt failed because all CCA attempts indicated that the channel was busy. */ +// PHY_TX_CCA_FAIL = 0x8D, +// /** +// * The transmit attempt was blocked from going over the air. +// * Typically this is due to the Radio Hold Off (RHO) or Coexistence plugins as they can prevent transmits based on external signals. +// */ +// PHY_TX_BLOCKED = 0x8E, +// /** The expected ACK was received after the last transmission. */ +// PHY_ACK_RECEIVED = 0x8F, + +// // Return Codes Passed to emberStackStatusHandler() See also ::emberStackStatusHandler = ,. +// /** The stack software has completed initialization and is ready to send and receive packets over the air. */ +// NETWORK_UP = 0x90, +// /** The network is not operating. */ +// NETWORK_DOWN = 0x91, +// /** An attempt to join a network failed. */ +// JOIN_FAILED = 0x94, +// /** After moving, a mobile node's attempt to re-establish contact with the network failed. */ +// MOVE_FAILED = 0x96, +// /** +// * An attempt to join as a router failed due to a Zigbee versus Zigbee Pro incompatibility. +// * Zigbee devices joining Zigbee Pro networks (or vice versa) must join as End Devices, not Routers. +// */ +// CANNOT_JOIN_AS_ROUTER = 0x98, +// /** The local node ID has changed. The application can get the new node ID by calling ::emberGetNodeId(). */ +// NODE_ID_CHANGED = 0x99, +// /** The local PAN ID has changed. The application can get the new PAN ID by calling ::emberGetPanId(). */ +// PAN_ID_CHANGED = 0x9A, +// /** The channel has changed. */ +// CHANNEL_CHANGED = 0x9B, +// /** The network has been opened for joining. */ +// NETWORK_OPENED = 0x9C, +// /** The network has been closed for joining. */ +// NETWORK_CLOSED = 0x9D, +// /** An attempt to join or rejoin the network failed because no router beacons could be heard by the joining node. */ +// NO_BEACONS = 0xAB, +// /** +// * An attempt was made to join a Secured Network using a pre-configured key, but the Trust Center sent back a +// * Network Key in-the-clear when an encrypted Network Key was required. (::EMBER_REQUIRE_ENCRYPTED_KEY). +// */ +// RECEIVED_KEY_IN_THE_CLEAR = 0xAC, +// /** An attempt was made to join a Secured Network, but the device did not receive a Network Key. */ +// NO_NETWORK_KEY_RECEIVED = 0xAD, +// /** After a device joined a Secured Network, a Link Key was requested (::EMBER_GET_LINK_KEY_WHEN_JOINING) but no response was ever received. */ +// NO_LINK_KEY_RECEIVED = 0xAE, +// /** +// * An attempt was made to join a Secured Network without a pre-configured key, +// * but the Trust Center sent encrypted data using a pre-configured key. +// */ +// PRECONFIGURED_KEY_REQUIRED = 0xAF, + +// // Security Errors +// /** The passed key data is not valid. A key of all zeros or all F's are reserved values and cannot be used. */ +// KEY_INVALID = 0xB2, +// /** The chosen security level (the value of ::EMBER_SECURITY_LEVEL) is not supported by the stack. */ +// INVALID_SECURITY_LEVEL = 0x95, +// /** +// * An error occurred when trying to encrypt at the APS Level. +// * +// * To APS encrypt an outgoing packet, the sender +// * needs to know the EUI64 of the destination. This error occurs because +// * the EUI64 of the destination can't be determined from +// * the short address (no entry in the neighbor, child, binding +// * or address tables). +// * +// * Every time this error code is seen, note that the stack initiates an +// * IEEE address discovery request behind the scenes. Responses +// * to the request are stored in the trust center cache portion of the +// * address table. Note that you need at least 1 entry allocated for +// * TC cache in the address table plugin. Depending on the available rows in +// * the table, newly discovered addresses may replace old ones. The address +// * table plugin is enabled by default on the host. If you are using an SoC +// * platform, please be sure to add the address table plugin. +// * +// * When customers choose to send APS messages by using short addresses, +// * they should incorporate a retry mechanism and try again, no sooner than +// * 2 seconds later, to resend the APS message. If the app always +// * receives 0xBE (IEEE_ADDRESS_DISCOVERY_IN_PROGRESS) after +// * multiple retries, that might indicate that: +// * a) destination node is not on the network +// * b) there are problems with the health of the network +// * c) there may not be any space set aside in the address table for +// * the newly discovered address - this can be rectified by reserving +// * more entries for the trust center cache in the address table plugin +// */ +// IEEE_ADDRESS_DISCOVERY_IN_PROGRESS = 0xBE, +// /** +// * An error occurred when trying to encrypt at the APS Level. +// * +// * This error occurs either because the long address of the recipient can't be +// * determined from the short address (no entry in the binding table) +// * or there is no link key entry in the table associated with the destination, +// * or there was a failure to load the correct key into the encryption core. +// */ +// APS_ENCRYPTION_ERROR = 0xA6, +// /** There was an attempt to form or join a network with security without calling ::emberSetInitialSecurityState() first. */ +// SECURITY_STATE_NOT_SET = 0xA8, +// /** +// * There was an attempt to set an entry in the key table using an invalid long address. Invalid addresses include: +// * - The local device's IEEE address +// * - Trust Center's IEEE address +// * - An existing table entry's IEEE address +// * - An address consisting of all zeros or all F's +// */ +// KEY_TABLE_INVALID_ADDRESS = 0xB3, +// /** There was an attempt to set a security configuration that is not valid given the other security settings. */ +// SECURITY_CONFIGURATION_INVALID = 0xB7, +// /** +// * There was an attempt to broadcast a key switch too quickly after broadcasting the next network key. +// * The Trust Center must wait at least a period equal to the broadcast timeout so that all routers have a chance +// * to receive the broadcast of the new network key. +// */ +// TOO_SOON_FOR_SWITCH_KEY = 0xB8, +// /** The received signature corresponding to the message that was passed to the CBKE Library failed verification and is not valid. */ +// SIGNATURE_VERIFY_FAILURE = 0xB9, +// /** +// * The message could not be sent because the link key corresponding to the destination is not authorized for use in APS data messages. +// * APS Commands (sent by the stack) are allowed. +// * To use it for encryption of APS data messages it must be authorized using a key agreement protocol (such as CBKE). +// */ +// KEY_NOT_AUTHORIZED = 0xBB, +// /** The security data provided was not valid, or an integrity check failed. */ +// SECURITY_DATA_INVALID = 0xBD, + +// // Miscellaneous Network Errors +// /** The node has not joined a network. */ +// NOT_JOINED = 0x93, +// /** A message cannot be sent because the network is currently overloaded. */ +// NETWORK_BUSY = 0xA1, +// /** The application tried to send a message using an endpoint that it has not defined. */ +// INVALID_ENDPOINT = 0xA3, +// /** The application tried to use a binding that has been remotely modified and the change has not yet been reported to the application. */ +// BINDING_HAS_CHANGED = 0xA4, +// /** An attempt to generate random bytes failed because of insufficient random data from the radio. */ +// INSUFFICIENT_RANDOM_DATA = 0xA5, +// /** A Zigbee route error command frame was received indicating that a source routed message from this node failed en route. */ +// SOURCE_ROUTE_FAILURE = 0xA9, +// /** +// * A Zigbee route error command frame was received indicating that a message sent to this node along a many-to-one route failed en route. +// * The route error frame was delivered by an ad-hoc search for a functioning route. +// */ +// MANY_TO_ONE_ROUTE_FAILURE = 0xAA, + +// // Miscellaneous Utility Errors +// /** +// * A critical and fatal error indicating that the version of the +// * stack trying to run does not match with the chip it's running on. The +// * software (stack) on the chip must be replaced with software +// * compatible with the chip. +// */ +// STACK_AND_HARDWARE_MISMATCH = 0xB0, +// /** An index was passed into the function that was larger than the valid range. */ +// INDEX_OUT_OF_RANGE = 0xB1, +// /** There are no empty entries left in the table. */ +// TABLE_FULL = 0xB4, +// /** The requested table entry has been erased and contains no valid data. */ +// TABLE_ENTRY_ERASED = 0xB6, +// /** The requested function cannot be executed because the library that contains the necessary functionality is not present. */ +// LIBRARY_NOT_PRESENT = 0xB5, +// /** The stack accepted the command and is currently processing the request. The results will be returned via an appropriate handler. */ +// OPERATION_IN_PROGRESS = 0xBA, +// /** +// * The EUI of the Trust center has changed due to a successful rejoin. +// * The device may need to perform other authentication to verify the new TC is authorized to take over. +// */ +// TRUST_CENTER_EUI_HAS_CHANGED = 0xBC, +// /** Trust center swapped out. The EUI has changed. */ +// TRUST_CENTER_SWAPPED_OUT_EUI_HAS_CHANGED = TRUST_CENTER_EUI_HAS_CHANGED, +// /** Trust center swapped out. The EUI has not changed. */ +// TRUST_CENTER_SWAPPED_OUT_EUI_HAS_NOT_CHANGED = 0xBF, + +// // NVM3 Token Errors +// /** NVM3 is telling the application that the initialization was aborted as no valid NVM3 page was found. */ +// NVM3_TOKEN_NO_VALID_PAGES = 0xC0, +// /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance was already opened with other parameters. */ +// NVM3_ERR_OPENED_WITH_OTHER_PARAMETERS = 0xC1, +// /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance is not aligned properly in memory. */ +// NVM3_ERR_ALIGNMENT_INVALID = 0xC2, +// /** NVM3 is telling the application that the initialization was aborted as the size of the NVM3 instance is too small. */ +// NVM3_ERR_SIZE_TOO_SMALL = 0xC3, +// /** NVM3 is telling the application that the initialization was aborted as the NVM3 page size is not supported. */ +// NVM3_ERR_PAGE_SIZE_NOT_SUPPORTED = 0xC4, +// /** NVM3 is telling the application that there was an error initializing some of the tokens. */ +// NVM3_ERR_TOKEN_INIT = 0xC5, +// /** NVM3 is telling the application there has been an error when attempting to upgrade SimEE tokens. */ +// NVM3_ERR_UPGRADE = 0xC6, +// /** NVM3 is telling the application that there has been an unknown error. */ +// NVM3_ERR_UNKNOWN = 0xC7, + +// // Application Errors. These error codes are available for application use. +// /** +// * This error is reserved for customer application use. +// * This will never be returned from any portion of the network stack or HAL. +// */ +// APPLICATION_ERROR_0 = 0xF0, +// APPLICATION_ERROR_1 = 0xF1, +// APPLICATION_ERROR_2 = 0xF2, +// APPLICATION_ERROR_3 = 0xF3, +// APPLICATION_ERROR_4 = 0xF4, +// APPLICATION_ERROR_5 = 0xF5, +// APPLICATION_ERROR_6 = 0xF6, +// APPLICATION_ERROR_7 = 0xF7, +// APPLICATION_ERROR_8 = 0xF8, +// APPLICATION_ERROR_9 = 0xF9, +// APPLICATION_ERROR_10 = 0xFA, +// APPLICATION_ERROR_11 = 0xFB, +// APPLICATION_ERROR_12 = 0xFC, +// APPLICATION_ERROR_13 = 0xFD, +// APPLICATION_ERROR_14 = 0xFE, +// APPLICATION_ERROR_15 = 0xFF, +// }; + /** Status values used by EZSP. */ export enum EzspStatus { /** Success. */ @@ -1277,6 +1387,25 @@ export enum EmberStackError { NETWORK_STATUS_UNKNOWN_COMMAND = 0x13 } +export enum EmberGPStatus { + /** Success Status */ + OK, + /** Match Frame */ + MATCH, + /** Drop Frame */ + DROP_FRAME, + /** Frame Unprocessed */ + UNPROCESSED, + /** Frame Pass Unprocessed */ + PASS_UNPROCESSED, + /** Frame TX Then Drop */ + TX_THEN_DROP, + /** No Security */ + NO_SECURITY, + /** Security Failure */ + AUTH_FAILURE, +} + /** Type of Ember software version */ export enum EmberVersionType { PRE_RELEASE = 0x00, @@ -2405,3 +2534,26 @@ export enum EmberGpSinkTableEntryStatus { /** The proxy table entry is not in use. */ UNUSED = 0xFF, }; + +export enum EmberLeaveNetworkOption { + /** Leave with no option. */ + WITH_NO_OPTION = 0x00, + /** Leave with option rejoin. */ + WITH_OPTION_REJOIN = 0x20, + /** Leave is requested. */ + IS_REQUESTED = 0x40, +}; + +/** + * Packet transmit priorities in terms of getting into the MAC queue. + * + * SL_802154_TRANSMIT_PRIORITY_HIGH High priority headers go on the front of the queue. + * SL_802154_TRANSMIT_PRIORITY_NORMAL Normal priority headers go on the back of the queue. + * SL_802154_TRANSMIT_PRIORITY_SCAN_OKAY Normally, only beacon requests and orphan notifications can be sent during a scan. + * They are submitted with SCAN_OKAY and go on the front of the queue. Other packets could be submitted with this priority, but it is not recommended. + */ +export enum EmberTransmitPriority { + HIGH = 0, + NORMAL = 1, + SCAN_OKAY = 2 +}; diff --git a/src/adapter/ember/ezsp/buffalo.ts b/src/adapter/ember/ezsp/buffalo.ts index c78ae1dbb3..324d7b7401 100644 --- a/src/adapter/ember/ezsp/buffalo.ts +++ b/src/adapter/ember/ezsp/buffalo.ts @@ -1,7 +1,7 @@ /* istanbul ignore file */ import Buffalo from "../../../buffalo/buffalo"; import {GP_SINK_LIST_ENTRIES} from "../consts"; -import {EmberGpApplicationId, EmberGpSinkType, EzspStatus} from "../enums"; +import {EmberGpApplicationId, EmberGpSinkType, EzspStatus, SLStatus} from "../enums"; import { EmberAesMmoHashContext, EmberApsFrame, @@ -14,6 +14,7 @@ import { EmberChildData, EmberCurrentSecurityState, EmberDutyCycleLimits, + EmberEndpointDescription, EmberGpAddress, EmberGpProxyTableEntry, EmberGpSinkListEntry, @@ -23,6 +24,7 @@ import { EmberMessageDigest, EmberMultiPhyRadioParameters, EmberMulticastTableEntry, + EmberMultiprotocolPriorities, EmberNeighborTableEntry, EmberNetworkInitStruct, EmberNetworkParameters, @@ -32,6 +34,7 @@ import { EmberPublicKey283k1Data, EmberPublicKeyData, EmberRouteTableEntry, + EmberRxPacketInfo, EmberSignature283k1Data, EmberSignatureData, EmberSmacData, @@ -79,6 +82,45 @@ import { } from "./consts"; import {EzspFrameID} from "./enums"; +/** + * Handle EmberStatus deprecation in v14+ for previous versions + */ +const EMBER_TO_SL_STATUS_MAP: Partial> = { + 25: SLStatus.ZIGBEE_PACKET_HANDOFF_DROPPED, + 102: SLStatus.ZIGBEE_DELIVERY_FAILED, + 114: SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED, + 116: SLStatus.MESSAGE_TOO_LONG, + 117: SLStatus.ZIGBEE_BINDING_IS_ACTIVE, + 118: SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE, + 144: SLStatus.NETWORK_UP, + 145: SLStatus.NETWORK_DOWN, + 147: SLStatus.NOT_JOINED, + 149: SLStatus.ZIGBEE_INVALID_SECURITY_LEVEL, + 150: SLStatus.ZIGBEE_MOVE_FAILED, + 153: SLStatus.ZIGBEE_NODE_ID_CHANGED, + 154: SLStatus.ZIGBEE_PAN_ID_CHANGED, + 155: SLStatus.ZIGBEE_CHANNEL_CHANGED, + 156: SLStatus.ZIGBEE_NETWORK_OPENED, + 157: SLStatus.ZIGBEE_NETWORK_CLOSED, + 161: SLStatus.BUSY, + 164: SLStatus.ZIGBEE_BINDING_HAS_CHANGED, + 166: SLStatus.ZIGBEE_APS_ENCRYPTION_ERROR, + 165: SLStatus.ZIGBEE_INSUFFICIENT_RANDOM_DATA, + 169: SLStatus.ZIGBEE_SOURCE_ROUTE_FAILURE, + 168: SLStatus.ZIGBEE_SECURITY_STATE_NOT_SET, + 170: SLStatus.ZIGBEE_MANY_TO_ONE_ROUTE_FAILURE, + 172: SLStatus.ZIGBEE_RECEIVED_KEY_IN_THE_CLEAR, + 173: SLStatus.ZIGBEE_NO_NETWORK_KEY_RECEIVED, + 174: SLStatus.ZIGBEE_NO_LINK_KEY_RECEIVED, + 175: SLStatus.ZIGBEE_PRECONFIGURED_KEY_REQUIRED, + 176: SLStatus.ZIGBEE_STACK_AND_HARDWARE_MISMATCH, + 184: SLStatus.ZIGBEE_TOO_SOON_FOR_SWITCH_KEY, + 185: SLStatus.ZIGBEE_SIGNATURE_VERIFY_FAILURE, + 187: SLStatus.ZIGBEE_KEY_NOT_AUTHORIZED, + 188: SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED, + 190: SLStatus.ZIGBEE_IEEE_ADDRESS_DISCOVERY_IN_PROGRESS, + 191: SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_NOT_CHANGED, +}; export class EzspBuffalo extends Buffalo { @@ -426,22 +468,22 @@ export class EzspBuffalo extends Buffalo { } public readSecManContext(): SecManContext { - const core_key_type = this.readUInt8(); - const key_index = this.readUInt8(); - const derived_type = this.readUInt16(); + const coreKeyType = this.readUInt8(); + const keyIndex = this.readUInt8(); + const derivedType = this.readUInt16(); const eui64 = this.readIeeeAddr(); - const multi_network_index = this.readUInt8(); + const multiNetworkIndex = this.readUInt8(); const flags = this.readUInt8(); - const psa_key_alg_permission = this.readUInt32(); + const psaKeyAlgPermission = this.readUInt32(); return { - coreKeyType: core_key_type, - keyIndex: key_index, - derivedType: derived_type, + coreKeyType, + keyIndex, + derivedType, eui64, - multiNetworkIndex: multi_network_index, + multiNetworkIndex, flags, - psaKeyAlgPermission: psa_key_alg_permission, + psaKeyAlgPermission, }; } @@ -478,15 +520,15 @@ export class EzspBuffalo extends Buffalo { public readSecManAPSKeyMetadata(): SecManAPSKeyMetadata { const bitmask = this.readUInt16(); - const outgoing_frame_counter = this.readUInt32(); - const incoming_frame_counter = this.readUInt32(); - const ttl_in_seconds = this.readUInt16(); + const outgoingFrameCounter = this.readUInt32(); + const incomingFrameCounter = this.readUInt32(); + const ttlInSeconds = this.readUInt16(); return { bitmask, - outgoingFrameCounter: outgoing_frame_counter, - incomingFrameCounter: incoming_frame_counter, - ttlInSeconds: ttl_in_seconds, + outgoingFrameCounter, + incomingFrameCounter, + ttlInSeconds, }; } @@ -1266,4 +1308,75 @@ export class EzspBuffalo extends Buffalo { this.writeUInt8(tokenInfo.size); this.writeUInt8(tokenInfo.arraySize); } + + /** + * EZSP switched to using SLStatus for command returns from version 14. + * @param version EZSP protocol version in use + * @param mapFromEmber If true, map from EmberStatus, otherwise map from EzspStatus + * @returns EzspStatus, EmberStatus or SLStatus as SLStatus + */ + public readStatus(version: number, mapFromEmber: boolean = true): SLStatus { + if (version < 0x0E) { + const status = this.readUInt8(); + + // map to SLStatus as appropriate + if (mapFromEmber) { + return EMBER_TO_SL_STATUS_MAP[status] ?? status; + } else { + return (status === SLStatus.OK) ? status : SLStatus.ZIGBEE_EZSP_ERROR; + } + } else { + return this.readUInt32(); + } + } + + public readEmberEndpointDescription(): EmberEndpointDescription { + const profileId = this.readUInt16(); + const deviceId = this.readUInt16(); + const deviceVersion = this.readUInt8(); + const inputClusterCount = this.readUInt8(); + const outputClusterCount = this.readUInt8(); + + return { + profileId, + deviceId, + deviceVersion, + inputClusterCount, + outputClusterCount, + }; + } + + public readEmberMultiprotocolPriorities(): EmberMultiprotocolPriorities { + const backgroundRx = this.readUInt8(); + const tx = this.readUInt8(); + const activeRx = this.readUInt8(); + + return {backgroundRx, tx, activeRx}; + } + + public writeEmberMultiprotocolPriorities(priorities: EmberMultiprotocolPriorities): void { + this.writeUInt8(priorities.backgroundRx); + this.writeUInt8(priorities.tx); + this.writeUInt8(priorities.activeRx); + } + + public readEmberRxPacketInfo(): EmberRxPacketInfo { + const senderShortId = this.readUInt16(); + const senderLongId = this.readIeeeAddr(); + const bindingIndex = this.readUInt8(); + const addressIndex = this.readUInt8(); + const lastHopLqi = this.readUInt8(); + const lastHopRssi = this.readInt8(); + const lastHopTimestamp = this.readUInt32(); + + return { + senderShortId, + senderLongId, + bindingIndex, + addressIndex, + lastHopLqi, + lastHopRssi, + lastHopTimestamp + }; + } } diff --git a/src/adapter/ember/ezsp/enums.ts b/src/adapter/ember/ezsp/enums.ts index 62221847c1..669b3efcd3 100644 --- a/src/adapter/ember/ezsp/enums.ts +++ b/src/adapter/ember/ezsp/enums.ts @@ -14,6 +14,16 @@ export enum EzspFrameID { GET_EXTENDED_VALUE = 0x0003, SET_VALUE = 0x00AB, SET_PASSIVE_ACK_CONFIG = 0x0105, + /** v14+ */ + SET_PENDING_NETWORK_UPDATE_PAN_ID = 0x011E, + /** v14+ */ + GET_ENDPOINT = 0x012E, + /** v14+ */ + GET_ENDPOINT_COUNT = 0x012F, + /** v14+ */ + GET_ENDPOINT_DESCRIPTION = 0x0130, + /** v14+ */ + GET_ENDPOINT_CLUSTER = 0x0131, // Utilities Frames NOP = 0x0005, @@ -43,9 +53,25 @@ export enum EzspFrameID { GET_NODE_ID = 0x0027, GET_PHY_INTERFACE_COUNT = 0x00FC, GET_TRUE_RANDOM_ENTROPY_SOURCE = 0x004F, + /** v14+ */ + SETUP_DELAYED_JOIN = 0x003A, + /** v14+ */ + RADIO_GET_SCHEDULER_PRIORITIES = 0x012A, + /** v14+ */ + RADIO_SET_SCHEDULER_PRIORITIES = 0x012B, + /** v14+ */ + RADIO_GET_SCHEDULER_SLIPTIME = 0x012C, + /** v14+ */ + RADIO_SET_SCHEDULER_SLIPTIME = 0x012D, + /** v14+ */ + COUNTER_REQUIRES_PHY_INDEX = 0x0132, + /** v14+ */ + COUNTER_REQUIRES_DESTINATION_NODE_ID = 0x0133, // Networking Frames SET_MANUFACTURER_CODE = 0x0015, + /** v14+ */ + GET_MANUFACTURER_CODE = 0x00CA, SET_POWER_DESCRIPTOR = 0x0016, NETWORK_INIT = 0x0017, NETWORK_STATE = 0x0018, @@ -68,9 +94,37 @@ export enum EzspFrameID { GET_NETWORK_PARAMETERS = 0x0028, GET_RADIO_PARAMETERS = 0x00FD, GET_PARENT_CHILD_PARAMETERS = 0x0029, + /** v14+ */ + ROUTER_CHILD_COUNT = 0x013B, + /** v14+ */ + MAX_CHILD_COUNT = 0x013C, + /** v14+ */ + MAX_ROUTER_CHILD_COUNT = 0x013D, + /** v14+ */ + GET_PARENT_INCOMING_NWK_FRAME_COUNTER = 0x013E, + /** v14+ */ + SET_PARENT_INCOMING_NWK_FRAME_COUNTER = 0x013F, + /** v14+ */ + CURRENT_STACK_TASKS = 0x0145, + /** v14+ */ + OK_TO_NAP = 0x0146, + /** v14+ */ + PARENT_TOKEN_SET = 0x0140, + /** v14+ */ + OK_TO_HIBERNATE = 0x0141, + /** v14+ */ + OK_TO_LONG_POLL = 0x0142, + /** v14+ */ + STACK_POWER_DOWN = 0x0143, + /** v14+ */ + STACK_POWER_UP = 0x0144, GET_CHILD_DATA = 0x004A, SET_CHILD_DATA = 0x00AC, CHILD_ID = 0x0106, + /** v14+ */ + CHILD_POWER = 0x0134, + /** v14+ */ + SET_CHILD_POWER = 0x0135, CHILD_INDEX = 0x0107, GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE = 0x00C3, GET_SOURCE_ROUTE_TABLE_FILLED_SIZE = 0x00C2, @@ -87,6 +141,12 @@ export enum EzspFrameID { GET_RADIO_CHANNEL = 0x00FF, SET_RADIO_IEEE802154_CCA_MODE = 0x0095, SET_CONCENTRATOR = 0x0010, + /** v14+ */ + CONCENTRATOR_START_DISCOVERY = 0x014F, + /** v14+ */ + CONCENTRATOR_STOP_DISCOVERY = 0x0150, + /** v14+ */ + CONCENTRATOR_NOTE_ROUTE_ERROR = 0x0151, SET_BROKEN_ROUTE_ERROR_CODE = 0x0011, MULTI_PHY_START = 0x00F8, MULTI_PHY_STOP = 0x00F9, @@ -98,11 +158,31 @@ export enum EzspFrameID { GET_DUTY_CYCLE_LIMITS = 0x004B, GET_CURRENT_DUTY_CYCLE = 0x004C, DUTY_CYCLE_HANDLER = 0x004D, - GET_FIRST_BEACON = 0x003D, - GET_NEXT_BEACON = 0x0004, + // GET_FIRST_BEACON = 0x003D,// v13-, unused + SET_NUM_BEACONS_TO_STORE = 0x0037, + // GET_NEXT_BEACON = 0x0004,// v13-, unused + GET_STORED_BEACON = 0x0004, GET_NUM_STORED_BEACONS = 0x0008, CLEAR_STORED_BEACONS = 0x003C, SET_LOGICAL_AND_RADIO_CHANNEL = 0x00B9, + /** v14+ */ + SLEEPY_TO_SLEEPY_NETWORK_START = 0x0119, + /** v14+ */ + SEND_ZIGBEE_LEAVE = 0x011A, + /** v14+ */ + GET_PERMIT_JOINING = 0x011F, + /** v14+ */ + GET_EXTENDED_PAN_ID = 0x0127, + /** v14+ */ + GET_CURRENT_NETWORK = 0x014E, + /** v14+ */ + SET_INITIAL_NEIGHBOR_OUTGOING_COST = 0x0122, + /** v14+ */ + GET_INITIAL_NEIGHBOR_OUTGOING_COST = 0x0123, + /** v14+ */ + RESET_REJOINING_NEIGHBORS_FRAME_COUNTER = 0x0124, + /** v14+ */ + IS_RESET_REJOINING_NEIGHBORS_FRAME_COUNTER_ENABLED = 0x0125, // Binding Frames CLEAR_BINDING_TABLE = 0x002A, @@ -119,16 +199,27 @@ export enum EzspFrameID { MAXIMUM_PAYLOAD_LENGTH = 0x0033, SEND_UNICAST = 0x0034, SEND_BROADCAST = 0x0036, - PROXY_BROADCAST = 0x0037, + // PROXY_BROADCAST = 0x0037,// v13-, unused + PROXY_NEXT_BROADCAST_FROM_LONG = 0x0066, SEND_MULTICAST = 0x0038, - SEND_MULTICAST_WITH_ALIAS = 0x003A, + // SEND_MULTICAST_WITH_ALIAS = 0x003A,// v13-, unused SEND_REPLY = 0x0039, MESSAGE_SENT_HANDLER = 0x003F, SEND_MANY_TO_ONE_ROUTE_REQUEST = 0x0041, POLL_FOR_DATA = 0x0042, POLL_COMPLETE_HANDLER = 0x0043, + /** v14+ */ + SET_MESSAGE_FLAG = 0x0136, + /** v14+ */ + CLEAR_MESSAGE_FLAG = 0x0137, POLL_HANDLER = 0x0044, - INCOMING_SENDER_EUI64_HANDLER = 0x0062, + /** v14+ */ + ADD_CHILD = 0x0138, + /** v14+ */ + REMOVE_CHILD = 0x0139, + /** v14+ */ + REMOVE_NEIGHBOR = 0x013A, + // INCOMING_SENDER_EUI64_HANDLER = 0x0062,// v13-, unused INCOMING_MESSAGE_HANDLER = 0x0045, SET_SOURCE_ROUTE_DISCOVERY_MODE = 0x005A, INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER = 0x007D, @@ -138,10 +229,14 @@ export enum EzspFrameID { SET_SOURCE_ROUTE = 0x00AE, UNICAST_CURRENT_NETWORK_KEY = 0x0050, ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x005B, - SET_ADDRESS_TABLE_REMOTE_EUI64 = 0x005C, - SET_ADDRESS_TABLE_REMOTE_NODE_ID = 0x005D, - GET_ADDRESS_TABLE_REMOTE_EUI64 = 0x005E, - GET_ADDRESS_TABLE_REMOTE_NODE_ID = 0x005F, + /** v14+ */ + SET_ADDRESS_TABLE_INFO = 0x005C, + /** v14+ */ + GET_ADDRESS_TABLE_INFO = 0x005E, + // SET_ADDRESS_TABLE_REMOTE_EUI64 = 0x005C,// v13-, unused + // SET_ADDRESS_TABLE_REMOTE_NODE_ID = 0x005D,// v13-, unused + // GET_ADDRESS_TABLE_REMOTE_EUI64 = 0x005E,// v13-, unused + // GET_ADDRESS_TABLE_REMOTE_NODE_ID = 0x005F,// v13-, unused SET_EXTENDED_TIMEOUT = 0x007E, GET_EXTENDED_TIMEOUT = 0x007F, REPLACE_ADDRESS_TABLE_ENTRY = 0x0082, @@ -151,14 +246,22 @@ export enum EzspFrameID { SET_MULTICAST_TABLE_ENTRY = 0x0064, ID_CONFLICT_HANDLER = 0x007C, WRITE_NODE_DATA = 0x00FE, - SEND_RAW_MESSAGE = 0x0096, - SEND_RAW_MESSAGE_EXTENDED = 0x0051, + // SEND_RAW_MESSAGE = 0x0096,// v13-, unused + SEND_RAW_MESSAGE = 0x0051, MAC_PASSTHROUGH_MESSAGE_HANDLER = 0x0097, MAC_FILTER_MATCH_MESSAGE_HANDLER = 0x0046, RAW_TRANSMIT_COMPLETE_HANDLER = 0x0098, SET_MAC_POLL_FAILURE_WAIT_TIME = 0x00F4, + /** v14+ */ + GET_MAX_MAC_RETRIES = 0x006A, SET_BEACON_CLASSIFICATION_PARAMS = 0x00EF, GET_BEACON_CLASSIFICATION_PARAMS = 0x00F3, + /** v14+ */ + PENDING_ACKED_MESSAGES = 0x0121, + /** v14+ */ + RESCHEDULE_LINK_STATUS_MSG = 0x011B, + /** v14+ */ + SET_NWK_UPDATE_ID = 0x011D, // Security Frames SET_INITIAL_SECURITY_STATE = 0x0068, @@ -183,6 +286,10 @@ export enum EzspFrameID { IMPORT_TRANSIENT_KEY = 0x0111, EXPORT_TRANSIENT_KEY_BY_INDEX = 0x0112, EXPORT_TRANSIENT_KEY_BY_EUI = 0x0113, + /** v14+ */ + SET_INCOMING_TC_LINK_KEY_FRAME_COUNTER = 0x0128, + /** v14+ */ + APS_CRYPT_MESSAGE = 0x0129, // Trust Center Frames TRUST_CENTER_JOIN_HANDLER = 0x0024, @@ -214,17 +321,17 @@ export enum EzspFrameID { SAVE_PREINSTALLED_CBKE_DATA283K1 = 0x00ED, // Mfglib Frames - MFGLIB_START = 0x0083, - MFGLIB_END = 0x0084, - MFGLIB_START_TONE = 0x0085, - MFGLIB_STOP_TONE = 0x0086, - MFGLIB_START_STREAM = 0x0087, - MFGLIB_STOP_STREAM = 0x0088, - MFGLIB_SEND_PACKET = 0x0089, - MFGLIB_SET_CHANNEL = 0x008a, - MFGLIB_GET_CHANNEL = 0x008b, - MFGLIB_SET_POWER = 0x008c, - MFGLIB_GET_POWER = 0x008d, + MFGLIB_INTERNAL_START = 0x0083, + MFGLIB_INTERNAL_END = 0x0084, + MFGLIB_INTERNAL_START_TONE = 0x0085, + MFGLIB_INTERNAL_STOP_TONE = 0x0086, + MFGLIB_INTERNAL_START_STREAM = 0x0087, + MFGLIB_INTERNAL_STOP_STREAM = 0x0088, + MFGLIB_INTERNAL_SEND_PACKET = 0x0089, + MFGLIB_INTERNAL_SET_CHANNEL = 0x008a, + MFGLIB_INTERNAL_GET_CHANNEL = 0x008b, + MFGLIB_INTERNAL_SET_POWER = 0x008c, + MFGLIB_INTERNAL_GET_POWER = 0x008d, MFGLIB_RX_HANDLER = 0x008e, // Bootloader Frames @@ -234,6 +341,20 @@ export enum EzspFrameID { INCOMING_BOOTLOAD_MESSAGE_HANDLER = 0x0092, BOOTLOAD_TRANSMIT_COMPLETE_HANDLER = 0x0093, AES_ENCRYPT = 0x0094, + /** v14+ */ + INCOMING_MFG_TEST_MESSAGE_HANDLER = 0x0147, + /** v14+ */ + MFG_TEST_SET_PACKET_MODE = 0x0148, + /** v14+ */ + MFG_TEST_SEND_REBOOT_COMMAND = 0x0149, + /** v14+ */ + MFG_TEST_SEND_EUI64 = 0x014A, + /** v14+ */ + MFG_TEST_SEND_MANUFACTURING_STRING = 0x014B, + /** v14+ */ + MFG_TEST_SEND_RADIO_PARAMETERS = 0x014C, + /** v14+ */ + MFG_TEST_SEND_COMMAND = 0x014D, // ZLL Frames ZLL_NETWORK_OPS = 0x00B2, @@ -263,12 +384,12 @@ export enum EzspFrameID { ZLL_CLEAR_TOKENS = 0x0025, // WWAH Frames - SET_PARENT_CLASSIFICATION_ENABLED = 0x00E7, - GET_PARENT_CLASSIFICATION_ENABLED = 0x00F0, - SET_LONG_UP_TIME = 0x00E3, - SET_HUB_CONNECTIVITY = 0x00E4, - IS_UP_TIME_LONG = 0x00E5, - IS_HUB_CONNECTED = 0x00E6, + // SET_PARENT_CLASSIFICATION_ENABLED = 0x00E7,// v13-, unused + // GET_PARENT_CLASSIFICATION_ENABLED = 0x00F0,// v13-, unused + // SET_LONG_UP_TIME = 0x00E3,// v13-, unused + // SET_HUB_CONNECTIVITY = 0x00E4,// v13-, unused + // IS_UP_TIME_LONG = 0x00E5,// v13-, unused + // IS_HUB_CONNECTED = 0x00E6,// v13-, unused // Green Power Frames GP_PROXY_TABLE_PROCESS_GP_PAIRING = 0x00C9, @@ -303,11 +424,10 @@ export enum EzspFrameID { export enum EzspConfigId { // 0x00? /** - * The NCP no longer supports configuration of packet buffer count at runtime - * using this parameter. Packet buffers must be configured using the - * EMBER_PACKET_BUFFER_COUNT macro when building the NCP project. + * The NCP no longer supports configuration of packet buffer heap at runtime using this parameter. + * Packet buffers heap space must be configured using the EMBER_PACKET_BUFFER_COUNT macro when building the NCP project. */ - PACKET_BUFFER_COUNT = 0x01, + PACKET_BUFFER_HEAP_SIZE = 0x01, /** * The maximum number of router neighbors the stack can keep track of. A * neighbor is a node within radio range. @@ -529,7 +649,13 @@ export enum EzspConfigId { /** This is green power proxy table size. This value is read-only and cannot be set at runtime */ GP_PROXY_TABLE_SIZE = 0x41, /** This is green power sink table size. This value is read-only and cannot be set at runtime */ - GP_SINK_TABLE_SIZE = 0x42 + GP_SINK_TABLE_SIZE = 0x42, + /** + * v14+ + * This is the configuration advertised by the end device to the parent when joining/rejoining, + * either SL_ZIGBEE_END_DEVICE_CONFIG_NONE or SL_ZIGBEE_END_DEVICE_CONFIG_PERSIST_DATA_ON_PARENT. + */ + END_DEVICE_CONFIGURATION = 0x43 } /** Identifies a policy decision. */ @@ -689,8 +815,8 @@ export enum EzspValueId { * EMBER_MAC_PASSTHROUGH_EMBERNET_SOURCE flag is set in MAC_PASSTHROUGH_FLAGS. */ EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02, - /** The number of available internal RAM general purpose buffers. Read only. */ - FREE_BUFFERS = 0x03, + /** The amount in bytes (max 2^16) of available general purpose heap memory. */ + BUFFER_HEAP_FREE_SIZE = 0x03, /** Selects sending synchronous callbacks in ezsp-uart. */ UART_SYNCH_CALLBACKS = 0x04, /** @@ -727,16 +853,7 @@ export enum EzspValueId { MFG_SECURITY_CONFIG = 0x10, /** Retrieves the version information from the stack on the NCP. */ VERSION_INFO = 0x11, - /** - * This will get/set the rejoin reason noted by the host for a subsequent call to emberFindAndRejoinNetwork(). - * After a call to emberFindAndRejoinNetwork() the host's rejoin reason will be set to EMBER_REJOIN_REASON_NONE. - * The NCP will store the rejoin reason used by the call to emberFindAndRejoinNetwork(). - * Application is not required to do anything with this value. - * The App Framework sets this for cases of emberFindAndRejoinNetwork that it initiates, but if the app is invoking a rejoin directly, - * it should/can set this value to aid in debugging of any rejoin state machine issues over EZSP logs after the fact. - * The NCP doesn't do anything with this value other than cache it so you can read it later. - */ - NEXT_HOST_REJOIN_REASON = 0x12, + // NEXT_HOST_REJOIN_REASON = 0x12,// v13-, unused /** * This is the reason that the last rejoin took place. This value may only be retrieved, not set. * The rejoin may have been initiated by the stack (NCP) or the application (host). @@ -818,7 +935,10 @@ export enum EzspValueId { TRANSIENT_KEY_TIMEOUT_S = 0x3B, /** Cumulative energy usage metric since the last value reset of the coulomb counter plugin. Setting this value will reset the coulomb counter. */ COULOMB_COUNTER_USAGE = 0x3C, - /** When scanning, configure the maximum number of beacons to store in cache. Each beacon consumes one packet buffer in RAM. */ + /** + * When scanning, configure the maximum number of beacons to store in cache. + * Each beacon consumes on average 32-bytes (+ buffer overhead) in RAM. + */ MAX_BEACONS_TO_STORE = 0x3D, /** Set the mask to filter out unacceptable child timeout options on a router. */ END_DEVICE_TIMEOUT_OPTIONS_MASK = 0x3E, @@ -842,7 +962,17 @@ export enum EzspValueId { */ KEY_STORAGE_VERSION = 0x44, /** Return activation state about TC Delayed Join on an NCP. A return value of 0 indicates that the feature is not activated. */ - DELAYED_JOIN_ACTIVATION = 0x45 + DELAYED_JOIN_ACTIVATION = 0x45, + /** + * v14+ + * The maximum number of NWK retries that will be attempted. + */ + MAX_NWK_RETRIES = 0x46, + /** + * v14+ + * Policies for allowing/disallowing rejoins. + */ + REJOIN_MODE = 0x47, } /** diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 4fb5d70f67..b6f6b8d32b 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -3,6 +3,7 @@ import EventEmitter from "events"; import {SerialPortOptions} from "../../tstype"; import {Clusters} from "../../../zspec/zcl/definition/cluster"; import * as ZSpec from "../../../zspec"; +import * as Zdo from "../../../zspec/zdo"; import {byteToBits, getMacCapFlags, highByte, highLowToInt, lowByte, lowHighBits} from "../utils/math"; import { EmberOutgoingMessageType, @@ -15,7 +16,6 @@ import { EmberMultiPhyNwkConfig, EmberNetworkStatus, EmberNodeType, - EmberStatus, EzspNetworkScanType, EzspStatus, SLStatus, @@ -35,13 +35,14 @@ import { EmberStackError, EmberInterpanMessageType, EmberGpApplicationId, + EmberLeaveNetworkOption, + EmberGPStatus, + EmberApsOption, + EmberTransmitPriority, } from "../enums"; import { EmberVersion, - EmberEUI64, - EmberPanId, EmberBeaconData, - EmberBeaconIterator, EmberBindingTableEntry, EmberChildData, EmberDutyCycleLimits, @@ -49,7 +50,6 @@ import { EmberNeighborTableEntry, EmberNetworkInitStruct, EmberNetworkParameters, - EmberNodeId, EmberPerDeviceDutyCycle, EmberRouteTableEntry, EmberApsFrame, @@ -83,7 +83,10 @@ import { EmberGpSinkTableEntry, EmberTokenInfo, EmberTokenData, - EmberZigbeeNetwork + EmberZigbeeNetwork, + EmberEndpointDescription, + EmberMultiprotocolPriorities, + EmberRxPacketInfo, } from "../types"; import { EmberZdoStatus, @@ -146,7 +149,6 @@ import { EZSP_FRAME_CONTROL_OVERFLOW, EZSP_FRAME_CONTROL_PENDING_CB_MASK, EZSP_FRAME_CONTROL_PENDING_CB, - EXTENDED_PAN_ID_SIZE, EMBER_ENCRYPTION_KEY_SIZE, } from "./consts"; import { @@ -179,6 +181,9 @@ import { } from "../consts"; import {FIXED_ENDPOINTS} from "../adapter/endpoints"; import {logger} from "../../../utils/logger"; +import {EUI64, ExtendedPanId, NodeId, PanId} from "../../../zspec/tstypes"; +import {EzspError} from "../ezspError"; +import {initSecurityManagerContext} from "../utils/initters"; const NS = 'zh:ember:ezsp'; @@ -218,25 +223,25 @@ export enum EzspEvents { NCP_NEEDS_RESET_AND_INIT = 'NCP_NEEDS_RESET_AND_INIT', //-- ezspIncomingMessageHandler - /** params => status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: { cluster-dependent @see zdo.ts } */ + /** params => status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: { cluster-dependent @see zdo.ts } */ ZDO_RESPONSE = 'ZDO_RESPONSE', - /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: EmberNodeId, messageContents: Buffer */ + /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: NodeId, messageContents: Buffer */ INCOMING_MESSAGE = 'INCOMING_MESSAGE', - /** params => sourcePanId: EmberPanId, sourceAddress: EmberEUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer */ + /** params => sourcePanId: PanId, sourceAddress: EUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer */ TOUCHLINK_MESSAGE = 'TOUCHLINK_MESSAGE', - /** params => sender: EmberNodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload */ + /** params => sender: NodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload */ END_DEVICE_ANNOUNCE = 'END_DEVICE_ANNOUNCE', //-- ezspStackStatusHandler - /** params => status: EmberStatus */ + /** params => status: SLStatus */ STACK_STATUS = 'STACK_STATUS', //-- ezspTrustCenterJoinHandler - /** params => newNodeId: EmberNodeId, newNodeEui64: EmberEUI64, status: EmberDeviceUpdate, policyDecision: EmberJoinDecision, parentOfNewNodeId: EmberNodeId */ + /** params => newNodeId: NodeId, newNodeEui64: EUI64, status: EmberDeviceUpdate, policyDecision: EmberJoinDecision, parentOfNewNodeId: NodeId */ TRUST_CENTER_JOIN = 'TRUST_CENTER_JOIN', //-- ezspMessageSentHandler - /** params => type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number, status: EmberStatus */ + /** params => status: SLStatus, type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number */ MESSAGE_SENT = 'MESSAGE_SENT', //-- ezspGpepIncomingMessageHandler @@ -375,6 +380,10 @@ export class Ezsp extends EventEmitter { logger.info(`======== EZSP stopped ========`, NS); } + /** + * Must be called immediately after `ezspVersion` to sync the Host protocol version. + * @param version Version for the Host to use. + */ public setProtocolVersion(version: number): void { this.version = version; } @@ -462,7 +471,7 @@ export class Ezsp extends EventEmitter { private startCommand(command: number): void { if (this.sendingCommand) { logger.error(`[SEND COMMAND] Cannot send second one before processing response from first one.`, NS); - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); + throw new EzspError(EzspStatus.ERROR_INVALID_CALL); } this.sendingCommand = true; @@ -545,11 +554,11 @@ export class Ezsp extends EventEmitter { logger.debug(`===> ${this.frameToString}`, NS); try { - status = await (new Promise((resolve, reject: (reason: Error) => void): void => { + status = await new Promise((resolve, reject: (reason: Error) => void): void => { const sendStatus = (this.ash.send(this.frameLength, this.frameContents)); if (sendStatus !== EzspStatus.SUCCESS) { - reject(new Error(EzspStatus[sendStatus])); + reject(new EzspError(sendStatus)); } const error = new Error(); @@ -565,10 +574,10 @@ export class Ezsp extends EventEmitter { }, this.ash.responseTimeout), resolve, }; - })); + }); if (status !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[status]); + throw new EzspError(status); } } catch (err) { logger.debug(`=x=> ${this.frameToString} Error: ${err}`, NS); @@ -727,7 +736,7 @@ export class Ezsp extends EventEmitter { break; } case EzspFrameID.STACK_STATUS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); this.ezspStackStatusHandler(status); break; } @@ -746,21 +755,21 @@ export class Ezsp extends EventEmitter { } case EzspFrameID.SCAN_COMPLETE_HANDLER: { const channel = this.buffalo.readUInt8(); - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); this.ezspScanCompleteHandler(channel, status); break; } case EzspFrameID.UNUSED_PAN_ID_FOUND_HANDLER: { - const panId: EmberPanId = this.buffalo.readUInt16(); + const panId: PanId = this.buffalo.readUInt16(); const channel = this.buffalo.readUInt8(); this.ezspUnusedPanIdFoundHandler(panId, channel); break; } case EzspFrameID.CHILD_JOIN_HANDLER: { - const index = this.buffalo.readUInt8(); - const joining: boolean = this.buffalo.readUInt8() === 1 ? true : false; - const childId: EmberNodeId = this.buffalo.readUInt16(); - const childEui64: EmberEUI64 = this.buffalo.readIeeeAddr(); + const index = this.buffalo.readUInt8(); + const joining = this.buffalo.readUInt8() !== 0; + const childId: NodeId = this.buffalo.readUInt16(); + const childEui64 = this.buffalo.readIeeeAddr(); const childType: EmberNodeType = this.buffalo.readUInt8(); this.ezspChildJoinHandler(index, joining, childId, childEui64, childType); break; @@ -777,85 +786,100 @@ export class Ezsp extends EventEmitter { case EzspFrameID.REMOTE_SET_BINDING_HANDLER: { const entry : EmberBindingTableEntry = this.buffalo.readEmberBindingTableEntry(); const index = this.buffalo.readUInt8(); - const policyDecision: EmberStatus = this.buffalo.readUInt8(); + const policyDecision = this.buffalo.readStatus(this.version); this.ezspRemoteSetBindingHandler(entry, index, policyDecision); break; } case EzspFrameID.REMOTE_DELETE_BINDING_HANDLER: { const index = this.buffalo.readUInt8(); - const policyDecision: EmberStatus = this.buffalo.readUInt8(); + const policyDecision = this.buffalo.readStatus(this.version); this.ezspRemoteDeleteBindingHandler(index, policyDecision); break; } case EzspFrameID.MESSAGE_SENT_HANDLER: { - const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); - const indexOrDestination = this.buffalo.readUInt16(); - const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); - const messageTag = this.buffalo.readUInt8(); - const status: EmberStatus = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspMessageSentHandler(type, indexOrDestination, apsFrame, messageTag, status, messageContents); + if (this.version < 0x0E) { + const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); + const indexOrDestination = this.buffalo.readUInt16(); + const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); + const messageTag = this.buffalo.readUInt8(); + const status = this.buffalo.readUInt8(); + const messageContents = this.buffalo.readPayload(); + + this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag, messageContents); + } else { + const status = this.buffalo.readUInt32(); + const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); + const indexOrDestination = this.buffalo.readUInt16(); + const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); + const messageTag = this.buffalo.readUInt16(); + const messageContents = this.buffalo.readPayload(); + + this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag, messageContents); + } break; } case EzspFrameID.POLL_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); this.ezspPollCompleteHandler(status); break; } case EzspFrameID.POLL_HANDLER: { - const childId: EmberNodeId = this.buffalo.readUInt16(); - const transmitExpected: boolean = this.buffalo.readUInt8() === 1 ? true : false; + const childId: NodeId = this.buffalo.readUInt16(); + const transmitExpected = this.buffalo.readUInt8() !== 0; this.ezspPollHandler(childId, transmitExpected); break; } - case EzspFrameID.INCOMING_SENDER_EUI64_HANDLER: { - const senderEui64: EmberEUI64 = this.buffalo.readIeeeAddr(); - this.ezspIncomingSenderEui64Handler(senderEui64); - break; - } case EzspFrameID.INCOMING_MESSAGE_HANDLER: { - const type: EmberIncomingMessageType = this.buffalo.readUInt8(); - const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const sender: EmberNodeId = this.buffalo.readUInt16(); - const bindingIndex = this.buffalo.readUInt8(); - const addressIndex = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspIncomingMessageHandler( - type, - apsFrame, - lastHopLqi, - lastHopRssi, - sender, - bindingIndex, - addressIndex, - messageContents - ); + if (this.version < 0x0E) { + const type: EmberIncomingMessageType = this.buffalo.readUInt8(); + const apsFrame = this.buffalo.readEmberApsFrame(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const senderShortId = this.buffalo.readUInt16(); + const bindingIndex = this.buffalo.readUInt8(); + const addressIndex = this.buffalo.readUInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex, + addressIndex, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0 + }; + const messageContents = this.buffalo.readPayload(); + this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); + } else { + const type: EmberIncomingMessageType = this.buffalo.readUInt8(); + const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const messageContents = this.buffalo.readPayload(); + this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); + } break; } case EzspFrameID.INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER: { - const source: EmberNodeId = this.buffalo.readUInt16(); - const longId: EmberEUI64 = this.buffalo.readIeeeAddr(); + const source: NodeId = this.buffalo.readUInt16(); + const longId = this.buffalo.readIeeeAddr(); const cost = this.buffalo.readUInt8(); this.ezspIncomingManyToOneRouteRequestHandler(source, longId, cost); break; } case EzspFrameID.INCOMING_ROUTE_ERROR_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - const target: EmberNodeId = this.buffalo.readUInt16(); + const status = this.buffalo.readStatus(this.version); + const target: NodeId = this.buffalo.readUInt16(); this.ezspIncomingRouteErrorHandler(status, target); break; } case EzspFrameID.INCOMING_NETWORK_STATUS_HANDLER: { const errorCode = this.buffalo.readUInt8(); - const target: EmberNodeId = this.buffalo.readUInt16(); + const target: NodeId = this.buffalo.readUInt16(); this.ezspIncomingNetworkStatusHandler(errorCode, target); break; } case EzspFrameID.INCOMING_ROUTE_RECORD_HANDLER: { - const source: EmberNodeId = this.buffalo.readUInt16(); - const sourceEui: EmberEUI64 = this.buffalo.readIeeeAddr(); + const source: NodeId = this.buffalo.readUInt16(); + const sourceEui = this.buffalo.readIeeeAddr(); const lastHopLqi = this.buffalo.readUInt8(); const lastHopRssi = this.buffalo.readInt8(); const relayCount = this.buffalo.readUInt8(); @@ -864,30 +888,69 @@ export class Ezsp extends EventEmitter { break; } case EzspFrameID.ID_CONFLICT_HANDLER: { - const id: EmberNodeId = this.buffalo.readUInt16(); + const id: NodeId = this.buffalo.readUInt16(); this.ezspIdConflictHandler(id); break; } case EzspFrameID.MAC_PASSTHROUGH_MESSAGE_HANDLER: { - const messageType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspMacPassthroughMessageHandler(messageType, lastHopLqi, lastHopRssi, messageContents); + if (this.version < 0x0E) { + const messageType: EmberMacPassthroughType = this.buffalo.readUInt8(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId: ZSpec.NULL_NODE_ID, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex: ZSpec.NULL_BINDING, + addressIndex: 0xFF, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0 + }; + const messageContents = this.buffalo.readPayload(); + this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents); + } else { + const messageType: EmberMacPassthroughType = this.buffalo.readUInt8(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const messageContents = this.buffalo.readPayload(); + this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents); + } break; } case EzspFrameID.MAC_FILTER_MATCH_MESSAGE_HANDLER: { - const filterIndexMatch = this.buffalo.readUInt8(); - const legacyPassthroughType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, lastHopLqi, lastHopRssi, messageContents); + if (this.version < 0x0E) { + const filterIndexMatch = this.buffalo.readUInt8(); + const legacyPassthroughType: EmberMacPassthroughType = this.buffalo.readUInt8(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId: ZSpec.NULL_NODE_ID, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex: ZSpec.NULL_BINDING, + addressIndex: 0xFF, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0 + }; + const messageContents = this.buffalo.readPayload(); + this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents); + } else { + const filterIndexMatch = this.buffalo.readUInt8(); + const legacyPassthroughType: EmberMacPassthroughType = this.buffalo.readUInt8(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const messageContents = this.buffalo.readPayload(); + this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents); + } break; } case EzspFrameID.RAW_TRANSMIT_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspRawTransmitCompleteHandler(status); + if (this.version < 0x0E) { + const status = this.buffalo.readUInt8(); + this.ezspRawTransmitCompleteHandler(Buffer.alloc(0), status); + } else { + const messageContents = this.buffalo.readPayload(); + const status = this.buffalo.readUInt32(); + this.ezspRawTransmitCompleteHandler(messageContents, status); + } break; } case EzspFrameID.SWITCH_NETWORK_KEY_HANDLER: { @@ -896,54 +959,54 @@ export class Ezsp extends EventEmitter { break; } case EzspFrameID.ZIGBEE_KEY_ESTABLISHMENT_HANDLER: { - const partner: EmberEUI64 = this.buffalo.readIeeeAddr(); + const partner = this.buffalo.readIeeeAddr(); const status: EmberKeyStatus = this.buffalo.readUInt8(); this.ezspZigbeeKeyEstablishmentHandler(partner, status); break; } case EzspFrameID.TRUST_CENTER_JOIN_HANDLER: { - const newNodeId: EmberNodeId = this.buffalo.readUInt16(); - const newNodeEui64: EmberEUI64 = this.buffalo.readIeeeAddr(); + const newNodeId: NodeId = this.buffalo.readUInt16(); + const newNodeEui64 = this.buffalo.readIeeeAddr(); const status: EmberDeviceUpdate = this.buffalo.readUInt8(); const policyDecision: EmberJoinDecision = this.buffalo.readUInt8(); - const parentOfNewNodeId: EmberNodeId = this.buffalo.readUInt16(); + const parentOfNewNodeId: NodeId = this.buffalo.readUInt16(); this.ezspTrustCenterJoinHandler(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId); break; } case EzspFrameID.GENERATE_CBKE_KEYS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const ephemeralPublicKey: EmberPublicKeyData = this.buffalo.readEmberPublicKeyData(); this.ezspGenerateCbkeKeysHandler(status, ephemeralPublicKey); break; } case EzspFrameID.CALCULATE_SMACS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const initiatorSmac: EmberSmacData = this.buffalo.readEmberSmacData(); const responderSmac: EmberSmacData = this.buffalo.readEmberSmacData(); this.ezspCalculateSmacsHandler(status, initiatorSmac, responderSmac); break; } case EzspFrameID.GENERATE_CBKE_KEYS_HANDLER283K1: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const ephemeralPublicKey: EmberPublicKey283k1Data = this.buffalo.readEmberPublicKey283k1Data(); this.ezspGenerateCbkeKeysHandler283k1(status, ephemeralPublicKey); break; } case EzspFrameID.CALCULATE_SMACS_HANDLER283K1: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const initiatorSmac: EmberSmacData = this.buffalo.readEmberSmacData(); const responderSmac: EmberSmacData = this.buffalo.readEmberSmacData(); this.ezspCalculateSmacsHandler283k1(status, initiatorSmac, responderSmac); break; } case EzspFrameID.DSA_SIGN_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const messageContents = this.buffalo.readPayload(); this.ezspDsaSignHandler(status, messageContents); break; } case EzspFrameID.DSA_VERIFY_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); this.ezspDsaVerifyHandler(status); break; } @@ -956,59 +1019,113 @@ export class Ezsp extends EventEmitter { break; } case EzspFrameID.INCOMING_BOOTLOAD_MESSAGE_HANDLER: { - const longId: EmberEUI64 = this.buffalo.readIeeeAddr(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspIncomingBootloadMessageHandler(longId, lastHopLqi, lastHopRssi, messageContents); + if (this.version < 0x0E) { + const longId = this.buffalo.readIeeeAddr(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId: ZSpec.NULL_NODE_ID, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex: ZSpec.NULL_BINDING, + addressIndex: 0xFF, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0, + }; + const messageContents = this.buffalo.readPayload(); + this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents); + } else { + const longId = this.buffalo.readIeeeAddr(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const messageContents = this.buffalo.readPayload(); + this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents); + } break; } case EzspFrameID.BOOTLOAD_TRANSMIT_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const messageContents = this.buffalo.readPayload(); this.ezspBootloadTransmitCompleteHandler(status, messageContents); break; } + case EzspFrameID.INCOMING_MFG_TEST_MESSAGE_HANDLER: { + const messageType = this.buffalo.readUInt8(); + const messageContents = this.buffalo.readPayload(); + this.ezspIncomingMfgTestMessageHandler(messageType, messageContents); + break; + } case EzspFrameID.ZLL_NETWORK_FOUND_HANDLER: { - const networkInfo: EmberZllNetwork = this.buffalo.readEmberZllNetwork(); - const isDeviceInfoNull: boolean = this.buffalo.readUInt8() === 1 ? true : false; - const deviceInfo: EmberZllDeviceInfoRecord = this.buffalo.readEmberZllDeviceInfoRecord(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, lastHopLqi, lastHopRssi); + if (this.version < 0x0E) { + const networkInfo = this.buffalo.readEmberZllNetwork(); + const isDeviceInfoNull = this.buffalo.readUInt8() !== 0; + const deviceInfo = this.buffalo.readEmberZllDeviceInfoRecord(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId: ZSpec.NULL_NODE_ID, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex: ZSpec.NULL_BINDING, + addressIndex: 0xFF, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0, + }; + this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo); + } else { + const networkInfo = this.buffalo.readEmberZllNetwork(); + const isDeviceInfoNull = this.buffalo.readUInt8() !== 0; + const deviceInfo = this.buffalo.readEmberZllDeviceInfoRecord(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo); + } break; } case EzspFrameID.ZLL_SCAN_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); this.ezspZllScanCompleteHandler(status); break; } case EzspFrameID.ZLL_ADDRESS_ASSIGNMENT_HANDLER: { - const addressInfo: EmberZllAddressAssignment = this.buffalo.readEmberZllAddressAssignment(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - this.ezspZllAddressAssignmentHandler(addressInfo, lastHopLqi, lastHopRssi); + if (this.version < 0x0E) { + const addressInfo = this.buffalo.readEmberZllAddressAssignment(); + const lastHopLqi = this.buffalo.readUInt8(); + const lastHopRssi = this.buffalo.readInt8(); + const packetInfo: EmberRxPacketInfo = { + senderShortId: ZSpec.NULL_NODE_ID, + senderLongId: ZSpec.BLANK_EUI64, + bindingIndex: ZSpec.NULL_BINDING, + addressIndex: 0xFF, + lastHopLqi, + lastHopRssi, + lastHopTimestamp: 0, + }; + this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo); + } else { + const addressInfo = this.buffalo.readEmberZllAddressAssignment(); + const packetInfo = this.buffalo.readEmberRxPacketInfo(); + this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo); + } break; } case EzspFrameID.ZLL_TOUCH_LINK_TARGET_HANDLER: { - const networkInfo: EmberZllNetwork = this.buffalo.readEmberZllNetwork(); + const networkInfo = this.buffalo.readEmberZllNetwork(); this.ezspZllTouchLinkTargetHandler(networkInfo); break; } case EzspFrameID.D_GP_SENT_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const gpepHandle = this.buffalo.readUInt8(); this.ezspDGpSentHandler(status, gpepHandle); break; } case EzspFrameID.GPEP_INCOMING_MESSAGE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); + const gpStatus = this.buffalo.readUInt8(); const gpdLink = this.buffalo.readUInt8(); const sequenceNumber = this.buffalo.readUInt8(); - const addr: EmberGpAddress = this.buffalo.readEmberGpAddress(); + const addr = this.buffalo.readEmberGpAddress(); const gpdfSecurityLevel: EmberGpSecurityLevel = this.buffalo.readUInt8(); const gpdfSecurityKeyType: EmberGpKeyType = this.buffalo.readUInt8(); - const autoCommissioning: boolean = this.buffalo.readUInt8() === 1 ? true : false; + const autoCommissioning = this.buffalo.readUInt8() !== 0; const bidirectionalInfo = this.buffalo.readUInt8(); const gpdSecurityFrameCounter = this.buffalo.readUInt32(); const gpdCommandId = this.buffalo.readUInt8(); @@ -1016,7 +1133,7 @@ export class Ezsp extends EventEmitter { const proxyTableIndex = this.buffalo.readUInt8(); const gpdCommandPayload = this.buffalo.readPayload(); this.ezspGpepIncomingMessageHandler( - status, + gpStatus, gpdLink, sequenceNumber, addr, @@ -1051,7 +1168,7 @@ export class Ezsp extends EventEmitter { * Alias types expect `alias` & `sequence` params, along with `apsFrame.radius`. * * @param type Specifies the outgoing message type. - * @param indexOrDestination uint16_t Depending on the type of addressing used, this is either the EmberNodeId of the destination, + * @param indexOrDestination uint16_t Depending on the type of addressing used, this is either the NodeId of the destination, * an index into the address table, or an index into the binding table. * Unused for multicast types. * This must be one of the three ZigBee broadcast addresses for broadcast. @@ -1059,15 +1176,17 @@ export class Ezsp extends EventEmitter { * @param message uint8_t * Content of the message. * @param alias The alias source address * @param sequence uint8_t The alias sequence number - * @returns Result of the ezspSend${x} call or EmberStatus.BAD_ARGUMENT if type not supported. + * @returns Result of the ezspSend${x} call or EmberStatus.INVALID_PARAMETER if type not supported. * @returns apsSequence as returned by ezspSend${x} command * @returns messageTag Tag used for ezspSend${x} command */ public async send(type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, message: Buffer, - alias: EmberNodeId, sequence: number): Promise<[EmberStatus, messageTag: number]> { - let status: EmberStatus = EmberStatus.BAD_ARGUMENT; + alias: NodeId, sequence: number): Promise<[SLStatus, messageTag: number]> { + let status: SLStatus = SLStatus.INVALID_PARAMETER; let apsSequence: number; const messageTag = this.nextSendSequence(); + let nwkRadius = ZA_MAX_HOPS; + let nwkAlias: NodeId = ZSpec.NULL_NODE_ID; switch (type) { case EmberOutgoingMessageType.VIA_BINDING: @@ -1076,45 +1195,41 @@ export class Ezsp extends EventEmitter { [status, apsSequence] = (await this.ezspSendUnicast(type, indexOrDestination, apsFrame, messageTag, message)); break; } - case EmberOutgoingMessageType.MULTICAST: { - [status, apsSequence] = (await this.ezspSendMulticast( - apsFrame, - ZA_MAX_HOPS/* hops */, - ZA_MAX_HOPS/* nonmember radius */, - messageTag, - message - )); - break; - } + case EmberOutgoingMessageType.MULTICAST: case EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: { - [status, apsSequence] = (await this.ezspSendMulticastWithAlias( + if (type === EmberOutgoingMessageType.MULTICAST_WITH_ALIAS || + (apsFrame.sourceEndpoint === ZSpec.GP_ENDPOINT && apsFrame.destinationEndpoint === ZSpec.GP_ENDPOINT && + (apsFrame.options & EmberApsOption.USE_ALIAS_SEQUENCE_NUMBER))) { + nwkRadius = apsFrame.radius; + nwkAlias = alias; + } + + [status, apsSequence] = (await this.ezspSendMulticast( apsFrame, - apsFrame.radius/*radius*/, - apsFrame.radius/*nonmember radius*/, - alias, + nwkRadius, + 0, // broadcast addr + nwkAlias, sequence, messageTag, message )); break; } - case EmberOutgoingMessageType.BROADCAST: { - [status, apsSequence] = (await this.ezspSendBroadcast( - indexOrDestination, - apsFrame, - ZA_MAX_HOPS/*radius*/, - messageTag, - message - )); - break; - } + case EmberOutgoingMessageType.BROADCAST: case EmberOutgoingMessageType.BROADCAST_WITH_ALIAS: { - [status, apsSequence] = (await this.ezspProxyBroadcast( - alias, + if (type == EmberOutgoingMessageType.BROADCAST_WITH_ALIAS || + (apsFrame.sourceEndpoint == ZSpec.GP_ENDPOINT && apsFrame.destinationEndpoint == ZSpec.GP_ENDPOINT && + (apsFrame.options & EmberApsOption.USE_ALIAS_SEQUENCE_NUMBER))) { + nwkRadius = apsFrame.radius; + nwkAlias = alias; + } + + [status, apsSequence] = (await this.ezspSendBroadcast( + nwkAlias, indexOrDestination, sequence, apsFrame, - apsFrame.radius, + nwkRadius, messageTag, message )); @@ -1128,7 +1243,7 @@ export class Ezsp extends EventEmitter { // NOTE: match `~~~>` from adapter since this is just a wrapper for it logger.debug( - `~~~> [SENT type=${EmberOutgoingMessageType[type]} apsSequence=${apsSequence} messageTag=${messageTag} status=${EmberStatus[status]}]`, + `~~~> [SENT type=${EmberOutgoingMessageType[type]} apsSequence=${apsSequence} messageTag=${messageTag} status=${SLStatus[status]}]`, NS, ); return [status, messageTag]; @@ -1140,11 +1255,11 @@ export class Ezsp extends EventEmitter { * @returns Send status * @returns EmberVersion*, null if status not SUCCESS. */ - public async ezspGetVersionStruct(): Promise<[EzspStatus, version: EmberVersion]> { + public async ezspGetVersionStruct(): Promise<[SLStatus, version: EmberVersion]> { const [status, outValueLength, outValue] = (await this.ezspGetValue(EzspValueId.VERSION_INFO, 7));// sizeof(EmberVersion) if (outValueLength !== 7) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, { @@ -1164,7 +1279,7 @@ export class Ezsp extends EventEmitter { * @param flags EzspEndpointFlags * @returns EzspStatus */ - public async ezspSetEndpointFlags(endpoint: number, flags: EzspEndpointFlag): Promise { + public async ezspSetEndpointFlags(endpoint: number, flags: EzspEndpointFlag): Promise { return this.ezspSetValue(EzspValueId.ENDPOINT_FLAGS, 3, [endpoint, lowByte(flags), highByte(flags)]); } @@ -1175,11 +1290,11 @@ export class Ezsp extends EventEmitter { * @returns EzspStatus * @returns flags */ - public async ezspGetEndpointFlags(endpoint: number): Promise<[EzspStatus, flags: EzspEndpointFlag]> { + public async ezspGetEndpointFlags(endpoint: number): Promise<[SLStatus, flags: EzspEndpointFlag]> { const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.ENDPOINT_FLAGS, endpoint, 2)); if (outValLen < 2) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } const returnFlags = highLowToInt(outVal[1], outVal[0]); @@ -1189,16 +1304,16 @@ export class Ezsp extends EventEmitter { /** * Wrapper for `ezspGetExtendedValue`. - * @param EmberNodeId + * @param NodeId * @param destination * @returns EzspStatus * @returns overhead uint8_t */ - public async ezspGetSourceRouteOverhead(destination: EmberNodeId): Promise<[EzspStatus, overhead: number]> { + public async ezspGetSourceRouteOverhead(destination: NodeId): Promise<[SLStatus, overhead: number]> { const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.GET_SOURCE_ROUTE_OVERHEAD, destination, 1)); if (outValLen < 1) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, outVal[0]]; @@ -1208,13 +1323,13 @@ export class Ezsp extends EventEmitter { * Wrapper for `ezspGetExtendedValue`. * @returns EzspStatus * @returns reason - * @returns nodeId EmberNodeId* + * @returns nodeId NodeId* */ - public async ezspGetLastLeaveReason(): Promise<[EzspStatus, reason: EmberLeaveReason, nodeId: EmberNodeId]> { + public async ezspGetLastLeaveReason(): Promise<[SLStatus, reason: EmberLeaveReason, nodeId: NodeId]> { const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.LAST_LEAVE_REASON, 0, 3)); if (outValLen < 3) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, outVal[0], highLowToInt(outVal[2], outVal[1])]; @@ -1225,11 +1340,11 @@ export class Ezsp extends EventEmitter { * @returns EzspStatus * @returns reason */ - public async ezspGetLastRejoinReason(): Promise<[EzspStatus, reason: EmberRejoinReason]> { + public async ezspGetLastRejoinReason(): Promise<[SLStatus, reason: EmberRejoinReason]> { const [status, outValLen, outVal] = (await this.ezspGetValue(EzspValueId.LAST_REJOIN_REASON, 1)); if (outValLen < 1) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, outVal[0]]; @@ -1240,7 +1355,7 @@ export class Ezsp extends EventEmitter { * @param mask * @returns */ - public async ezspSetExtendedSecurityBitmask(mask: EmberExtendedSecurityBitmask): Promise { + public async ezspSetExtendedSecurityBitmask(mask: EmberExtendedSecurityBitmask): Promise { return this.ezspSetValue(EzspValueId.EXTENDED_SECURITY_BITMASK, 2, [lowByte(mask), highByte(mask)]); } @@ -1248,11 +1363,11 @@ export class Ezsp extends EventEmitter { * Wrapper for `ezspGetValue`. * @returns */ - public async ezspGetExtendedSecurityBitmask(): Promise<[EzspStatus, mask: EmberExtendedSecurityBitmask]> { + public async ezspGetExtendedSecurityBitmask(): Promise<[SLStatus, mask: EmberExtendedSecurityBitmask]> { const [status, outValLen, outVal] = (await this.ezspGetValue(EzspValueId.EXTENDED_SECURITY_BITMASK, 2)); if (outValLen < 2) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, highLowToInt(outVal[1], outVal[0])]; @@ -1262,7 +1377,7 @@ export class Ezsp extends EventEmitter { * Wrapper for `ezspSetValue`. * @returns */ - public async ezspStartWritingStackTokens(): Promise { + public async ezspStartWritingStackTokens(): Promise { return this.ezspSetValue(EzspValueId.STACK_TOKEN_WRITING, 1, [1]); } @@ -1270,7 +1385,7 @@ export class Ezsp extends EventEmitter { * Wrapper for `ezspSetValue`. * @returns */ - public async ezspStopWritingStackTokens(): Promise { + public async ezspStopWritingStackTokens(): Promise { return this.ezspSetValue(EzspValueId.STACK_TOKEN_WRITING, 1, [0]); } @@ -1298,15 +1413,15 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.VERSION); this.buffalo.writeUInt8(desiredProtocolVersion); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const protocolVersion: number = this.buffalo.readUInt8(); - const stackType: number = this.buffalo.readUInt8(); - const stackVersion: number = this.buffalo.readUInt16(); + const protocolVersion = this.buffalo.readUInt8(); + const stackType = this.buffalo.readUInt8(); + const stackVersion = this.buffalo.readUInt16(); return [protocolVersion, stackType, stackVersion]; } @@ -1320,18 +1435,18 @@ export class Ezsp extends EventEmitter { * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize configId. * - uint16_t * The configuration value. */ - async ezspGetConfigurationValue(configId: EzspConfigId): Promise<[EzspStatus, value: number]> { + async ezspGetConfigurationValue(configId: EzspConfigId): Promise<[SLStatus, value: number]> { this.startCommand(EzspFrameID.GET_CONFIGURATION_VALUE); this.buffalo.writeUInt8(configId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); - const value: number = this.buffalo.readUInt16(); + const status = this.buffalo.readStatus(this.version, false); + const value = this.buffalo.readUInt16(); return [status, value]; } @@ -1351,18 +1466,18 @@ export class Ezsp extends EventEmitter { * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize configId, * - EzspStatus.ERROR_INVALID_CALL if configuration values can no longer be modified. */ - async ezspSetConfigurationValue(configId: EzspConfigId, value: number): Promise { + async ezspSetConfigurationValue(configId: EzspConfigId, value: number): Promise { this.startCommand(EzspFrameID.SET_CONFIGURATION_VALUE); this.buffalo.writeUInt8(configId); this.buffalo.writeUInt16(value); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); return status; } @@ -1381,7 +1496,7 @@ export class Ezsp extends EventEmitter { * - uint8_t * Attribute data. */ async ezspReadAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, readLength: number): - Promise<[EmberStatus, dataType: number, outReadLength: number, data: number[]]> { + Promise<[SLStatus, dataType: number, outReadLength: number, data: number[]]> { this.startCommand(EzspFrameID.READ_ATTRIBUTE); this.buffalo.writeUInt8(endpoint); this.buffalo.writeUInt16(cluster); @@ -1389,19 +1504,19 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(mask); this.buffalo.writeUInt16(manufacturerCode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const maxReadLength = readLength; - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(0);// XXX: not yet switched to uint32 in v14, trick with 0 version for proper mapping const dataType = this.buffalo.readUInt8(); readLength = this.buffalo.readUInt8(); if (readLength > maxReadLength) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } const data = this.buffalo.readListUInt8(readLength); @@ -1423,7 +1538,7 @@ export class Ezsp extends EventEmitter { * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. */ async ezspWriteAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, - overrideReadOnlyAndDataType: boolean, justTest: boolean, dataType: number, data: Buffer): Promise { + overrideReadOnlyAndDataType: boolean, justTest: boolean, dataType: number, data: Buffer): Promise { this.startCommand(EzspFrameID.WRITE_ATTRIBUTE); this.buffalo.writeUInt8(endpoint); this.buffalo.writeUInt16(cluster); @@ -1435,13 +1550,13 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(dataType); this.buffalo.writePayload(data); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readUInt8();// XXX: not yet switched to uint32 in v14 return status; } @@ -1464,7 +1579,7 @@ export class Ezsp extends EventEmitter { * - EzspStatus.ERROR_INVALID_CALL if endpoints can no longer be added. */ async ezspAddEndpoint(endpoint: number, profileId: number, deviceId: number, deviceVersion: number, - inputClusterList: number[], outputClusterList: number[]): Promise { + inputClusterList: number[], outputClusterList: number[]): Promise { this.startCommand(EzspFrameID.ADD_ENDPOINT); this.buffalo.writeUInt8(endpoint); this.buffalo.writeUInt16(profileId); @@ -1475,13 +1590,13 @@ export class Ezsp extends EventEmitter { this.buffalo.writeListUInt16(inputClusterList); this.buffalo.writeListUInt16(outputClusterList); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); return status; } @@ -1495,18 +1610,18 @@ export class Ezsp extends EventEmitter { * - EzspStatus.SUCCESS if the policy was changed, * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize policyId. */ - async ezspSetPolicy(policyId: EzspPolicyId, decisionId: number): Promise { + async ezspSetPolicy(policyId: EzspPolicyId, decisionId: number): Promise { this.startCommand(EzspFrameID.SET_POLICY); this.buffalo.writeUInt8(policyId); this.buffalo.writeUInt8(decisionId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); return status; } @@ -1519,18 +1634,18 @@ export class Ezsp extends EventEmitter { * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize policyId. * - EzspDecisionId * The current decision for the specified policy. */ - async ezspGetPolicy(policyId: EzspPolicyId): Promise<[EzspStatus, number]> { + async ezspGetPolicy(policyId: EzspPolicyId): Promise<[SLStatus, number]> { this.startCommand(EzspFrameID.GET_POLICY); this.buffalo.writeUInt8(policyId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); - const decisionId: number = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); + const decisionId = this.buffalo.readUInt8(); return [status, decisionId]; } @@ -1540,17 +1655,17 @@ export class Ezsp extends EventEmitter { * @param The new Pan Id * @returns true if the request was successfully handed to the stack, false otherwise */ - async ezspSendPanIdUpdate(newPan: EmberPanId): Promise { + async ezspSendPanIdUpdate(newPan: PanId): Promise { this.startCommand(EzspFrameID.SEND_PAN_ID_UPDATE); this.buffalo.writeUInt16(newPan); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: boolean = this.buffalo.readUInt8() === 1 ? true : false; + const status = this.buffalo.readUInt8() !== 0; return status; } @@ -1568,24 +1683,22 @@ export class Ezsp extends EventEmitter { * - uint8_t * The value. */ async ezspGetValue(valueId: EzspValueId, valueLength: number): - Promise<[EzspStatus, outValueLength: number, outValue: number[]]> { + Promise<[SLStatus, outValueLength: number, outValue: number[]]> { this.startCommand(EzspFrameID.GET_VALUE); this.buffalo.writeUInt8(valueId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - // let value: number[] = null; - const maxValueLength = valueLength; - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); valueLength = this.buffalo.readUInt8(); if (valueLength > maxValueLength) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } const value = this.buffalo.readListUInt8(valueLength); @@ -1608,25 +1721,25 @@ export class Ezsp extends EventEmitter { * - uint8_t * The value. */ async ezspGetExtendedValue(valueId: EzspExtendedValueId, characteristics: number, valueLength: number): - Promise<[EzspStatus, outValueLength: number, outValue: number[]]> { + Promise<[SLStatus, outValueLength: number, outValue: number[]]> { this.startCommand(EzspFrameID.GET_EXTENDED_VALUE); this.buffalo.writeUInt8(valueId); this.buffalo.writeUInt32(characteristics); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } // let value: number[] = null; const maxValueLength = valueLength; - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); valueLength = this.buffalo.readUInt8(); if (valueLength > maxValueLength) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } const value = this.buffalo.readListUInt8(valueLength); @@ -1645,19 +1758,19 @@ export class Ezsp extends EventEmitter { * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize valueId, * - EzspStatus.ERROR_INVALID_CALL if the value could not be modified. */ - async ezspSetValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { + async ezspSetValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { this.startCommand(EzspFrameID.SET_VALUE); this.buffalo.writeUInt8(valueId); this.buffalo.writeUInt8(valueLength); this.buffalo.writeListUInt8(value); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EzspStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version, false); return status; } @@ -1670,22 +1783,135 @@ export class Ezsp extends EventEmitter { * deeming the broadcast transmission complete. * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. */ - async ezspSetPassiveAckConfig(config: number, minAcksNeeded: number): Promise { + async ezspSetPassiveAckConfig(config: number, minAcksNeeded: number): Promise { this.startCommand(EzspFrameID.SET_PASSIVE_ACK_CONFIG); this.buffalo.writeUInt8(config); this.buffalo.writeUInt8(minAcksNeeded); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } + /** + * Set the PAN ID to be accepted by the device in a NLME Network Update command. + * If this is set to a different value than its default 0xFFFF, NLME network update messages will be ignored if they do not match this PAN ID. + * @param panId uint16_t PAN ID to be accepted in a network update. + */ + async ezspSetPendingNetworkUpdatePanId(panId: PanId): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_PENDING_NETWORK_UPDATE_PAN_ID); + this.buffalo.writeUInt16(panId); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Retrieve the endpoint number located at the specified index. + * @param index uint8_t Index to retrieve the endpoint number for. + * @returns uint8_t Endpoint number at the index. + */ + async ezspGetEndpoint(index: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_ENDPOINT); + this.buffalo.writeUInt8(index); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const endpoint = this.buffalo.readUInt8(); + + return endpoint; + } + + /** + * Get the number of configured endpoints. + * @returns uint8_t Number of configured endpoints. + */ + async ezspGetEndpointCount(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_ENDPOINT_COUNT); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const count = this.buffalo.readUInt8(); + + return count; + } + + /** + * Retrieve the endpoint description for the given endpoint number. + * @param endpoint Endpoint number to get the description of. + * @returns Description of this endpoint. + */ + async ezspGetEndpointDescription(endpoint: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_ENDPOINT_DESCRIPTION); + this.buffalo.writeUInt8(endpoint); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const endpointDescription = this.buffalo.readEmberEndpointDescription(); + + return endpointDescription; + } + + /** + * Retrieve one of the cluster IDs associated with the given endpoint. + * @param endpoint Endpoint number to get a cluster ID for. + * @param listId Which list to get the cluster ID from. (0 for input, 1 for output). + * @param listIndex Index from requested list to look at the cluster ID of. + * @returns ID of the requested cluster. + */ + async ezspGetEndpointCluster(endpoint: number, listId: number, listIndex: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_ENDPOINT_CLUSTER); + this.buffalo.writeUInt8(endpoint); + this.buffalo.writeUInt8(listId); + this.buffalo.writeUInt8(listIndex); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const endpointCluster = this.buffalo.readUInt16(); + + return endpointCluster; + } + //----------------------------------------------------------------------------- // Utilities Frames //----------------------------------------------------------------------------- @@ -1695,10 +1921,10 @@ export class Ezsp extends EventEmitter { async ezspNop(): Promise { this.startCommand(EzspFrameID.NOP); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -1714,16 +1940,16 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.ECHO); this.buffalo.writePayload(data); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const echo = this.buffalo.readPayload(); if (echo.length > data.length) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return echo; @@ -1735,10 +1961,10 @@ export class Ezsp extends EventEmitter { async ezspCallback(): Promise { this.startCommand(EzspFrameID.CALLBACK); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } this.callbackDispatch(); @@ -1758,18 +1984,18 @@ export class Ezsp extends EventEmitter { * @param tokenData uint8_t * The data to write to the token. * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. */ - async ezspSetToken(tokenId: number, tokenData: number[]): Promise { + async ezspSetToken(tokenId: number, tokenData: number[]): Promise { this.startCommand(EzspFrameID.SET_TOKEN); this.buffalo.writeUInt8(tokenId); this.buffalo.writeListUInt8(tokenData); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -1781,17 +2007,17 @@ export class Ezsp extends EventEmitter { * - An EmberStatus value indicating success or the reason for failure. * - uint8_t * The contents of the token. */ - async ezspGetToken(tokenId: number): Promise<[EmberStatus, tokenData: number[]]> { + async ezspGetToken(tokenId: number): Promise<[SLStatus, tokenData: number[]]> { this.startCommand(EzspFrameID.GET_TOKEN); this.buffalo.writeUInt8(tokenId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const tokenData = this.buffalo.readListUInt8(8); return [status, tokenData]; @@ -1809,10 +2035,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GET_MFG_TOKEN); this.buffalo.writeUInt8(tokenId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const tokenDataLength = this.buffalo.readUInt8(); @@ -1855,7 +2081,7 @@ export class Ezsp extends EventEmitter { } if (tokenDataLength != expectedTokenDataLength) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } const tokenData = this.buffalo.readListUInt8(tokenDataLength); @@ -1872,18 +2098,18 @@ export class Ezsp extends EventEmitter { * @param tokenData uint8_t * The manufacturing token data. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetMfgToken(tokenId: EzspMfgTokenId, tokenData: Buffer): Promise { + async ezspSetMfgToken(tokenId: EzspMfgTokenId, tokenData: Buffer): Promise { this.startCommand(EzspFrameID.SET_MFG_TOKEN); this.buffalo.writeUInt8(tokenId); this.buffalo.writePayload(tokenData); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -1903,16 +2129,16 @@ export class Ezsp extends EventEmitter { * - Always returns EMBER_SUCCESS. * - uint16_t * A pseudorandom number. */ - async ezspGetRandomNumber(): Promise<[EmberStatus, value: number]> { + async ezspGetRandomNumber(): Promise<[SLStatus, value: number]> { this.startCommand(EzspFrameID.GET_RANDOM_NUMBER); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const value = this.buffalo.readUInt16(); return [status, value]; @@ -1929,20 +2155,21 @@ export class Ezsp extends EventEmitter { * @param repeat If true, a timerHandler callback will be generated repeatedly. If false, only a single timerHandler callback will be generated. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetTimer(timerId: number, time: number, units: EmberEventUnits, repeat: boolean): Promise { + async ezspSetTimer(timerId: number, time: number, units: EmberEventUnits, repeat: boolean): Promise { this.startCommand(EzspFrameID.SET_TIMER); this.buffalo.writeUInt8(timerId); this.buffalo.writeUInt16(time); this.buffalo.writeUInt8(units); this.buffalo.writeUInt8(repeat ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -1960,15 +2187,15 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GET_TIMER); this.buffalo.writeUInt8(timerId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const time = this.buffalo.readUInt16(); const units = this.buffalo.readUInt8(); - const repeat = this.buffalo.readUInt8() === 1 ? true : false; + const repeat = this.buffalo.readUInt8() !== 0; return [time, units, repeat]; } @@ -1988,18 +2215,18 @@ export class Ezsp extends EventEmitter { * @param messageContents uint8_t * The binary message. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspDebugWrite(binaryMessage: boolean, messageContents: Buffer): Promise { + async ezspDebugWrite(binaryMessage: boolean, messageContents: Buffer): Promise { this.startCommand(EzspFrameID.DEBUG_WRITE); this.buffalo.writeUInt8(binaryMessage ? 1 : 0); this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2011,10 +2238,10 @@ export class Ezsp extends EventEmitter { async ezspReadAndClearCounters(): Promise { this.startCommand(EzspFrameID.READ_AND_CLEAR_COUNTERS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const values = this.buffalo.readListUInt16(EmberCounterType.COUNT); @@ -2029,10 +2256,10 @@ export class Ezsp extends EventEmitter { async ezspReadCounters(): Promise { this.startCommand(EzspFrameID.READ_COUNTERS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const values = this.buffalo.readListUInt16(EmberCounterType.COUNT); @@ -2058,10 +2285,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.DELAY_TEST); this.buffalo.writeUInt16(delay); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -2074,13 +2301,13 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GET_LIBRARY_STATUS); this.buffalo.writeUInt8(libraryId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberLibraryStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readUInt8(); return status; } @@ -2095,16 +2322,16 @@ export class Ezsp extends EventEmitter { * - manufacturerId uint16_t * The manufactured ID the user has defined in the XNCP application. * - versionNumber uint16_t * The version number of the XNCP application. */ - async ezspGetXncpInfo(): Promise<[EmberStatus, manufacturerId: number, versionNumber: number]> { + async ezspGetXncpInfo(): Promise<[SLStatus, manufacturerId: number, versionNumber: number]> { this.startCommand(EzspFrameID.GET_XNCP_INFO); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const manufacturerId = this.buffalo.readUInt16(); const versionNumber = this.buffalo.readUInt16(); @@ -2122,22 +2349,21 @@ export class Ezsp extends EventEmitter { * - The status returned by the custom command. * - uint8_t *The response. */ - async ezspCustomFrame(payload: Buffer, replyLength: number): - Promise<[EmberStatus, outReply: Buffer]> { + async ezspCustomFrame(payload: Buffer, replyLength: number): Promise<[SLStatus, outReply: Buffer]> { this.startCommand(EzspFrameID.CUSTOM_FRAME); this.buffalo.writePayload(payload); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const reply = this.buffalo.readPayload(); if (reply.length > replyLength) { - throw EzspStatus.ERROR_INVALID_VALUE; + throw new EzspError(EzspStatus.ERROR_INVALID_VALUE); } return [status, reply]; @@ -2157,13 +2383,13 @@ export class Ezsp extends EventEmitter { * Returns the EUI64 ID of the local node. * @returns The 64-bit ID. */ - async ezspGetEui64(): Promise { + async ezspGetEui64(): Promise { this.startCommand(EzspFrameID.GET_EUI64); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const eui64 = this.buffalo.readIeeeAddr(); @@ -2175,16 +2401,16 @@ export class Ezsp extends EventEmitter { * Returns the 16-bit node ID of the local node. * @returns The 16-bit ID. */ - async ezspGetNodeId(): Promise { + async ezspGetNodeId(): Promise { this.startCommand(EzspFrameID.GET_NODE_ID); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const nodeId: EmberNodeId = this.buffalo.readUInt16(); + const nodeId = this.buffalo.readUInt16(); return nodeId; } @@ -2196,10 +2422,10 @@ export class Ezsp extends EventEmitter { async ezspGetPhyInterfaceCount(): Promise { this.startCommand(EzspFrameID.GET_PHY_INTERFACE_COUNT); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const interfaceCount = this.buffalo.readUInt8(); @@ -2213,18 +2439,173 @@ export class Ezsp extends EventEmitter { */ async ezspGetTrueRandomEntropySource(): Promise { this.startCommand(EzspFrameID.GET_TRUE_RANDOM_ENTROPY_SOURCE); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const entropySource: EmberEntropySource = this.buffalo.readUInt8(); + + const entropySource = this.buffalo.readUInt8(); return entropySource; } + /** + * Extend a joiner's timeout to wait for the network key on the joiner default key timeout is 3 sec, + * and only values greater equal to 3 sec are accepted. + * @param networkKeyTimeoutS Network key timeout + * @returns An SLStatus value indicating success or the reason for failure. + */ + async ezspSetupDelayedJoin(networkKeyTimeoutS: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SETUP_DELAYED_JOIN); + this.buffalo.writeUInt8(networkKeyTimeoutS); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Get the current scheduler priorities for multiprotocol apps. + * @returns The current priorities. + */ + async ezspRadioGetSchedulerPriorities(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RADIO_GET_SCHEDULER_PRIORITIES); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const priorities = this.buffalo.readEmberMultiprotocolPriorities(); + + return priorities; + } + + /** + * Set the current scheduler priorities for multiprotocol apps. + * @param priorities The current priorities. + */ + async ezspRadioSetSchedulerPriorities(priorities: EmberMultiprotocolPriorities): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RADIO_SET_SCHEDULER_PRIORITIES); + this.buffalo.writeEmberMultiprotocolPriorities(priorities); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Get the current multiprotocol sliptime + * @returns Value of the current slip time. + */ + async ezspRadioGetSchedulerSliptime(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RADIO_GET_SCHEDULER_SLIPTIME); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const slipTime = this.buffalo.readUInt32(); + + return slipTime; + } + + /** + * Set the current multiprotocol sliptime + * @param slipTime Value of the current slip time. + */ + async ezspRadioSetSchedulerSliptime(slipTime: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RADIO_SET_SCHEDULER_SLIPTIME); + this.buffalo.writeUInt32(slipTime); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Check if a particular counter is one that could report from either a 2.4GHz or sub-GHz interface. + * @param counter The counter to be checked. + * @returns Whether this counter requires a PHY index when operating on a dual-PHY system. + */ + async ezspCounterRequiresPhyIndex(counter: EmberCounterType): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.COUNTER_REQUIRES_PHY_INDEX); + this.buffalo.writeUInt8(counter); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const requires = this.buffalo.readUInt8() !== 0; + + return requires; + } + + /** + * Check if a particular counter can report on the destination node ID they have been triggered from. + * @param counter The counter to be checked. + * @returns Whether this counter requires the destination node ID. + */ + async ezspCounterRequiresDestinationNodeId(counter: EmberCounterType): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.COUNTER_REQUIRES_DESTINATION_NODE_ID); + this.buffalo.writeUInt8(counter); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const requires = this.buffalo.readUInt8() !== 0; + + return requires; + } + //----------------------------------------------------------------------------- // Networking Frames //----------------------------------------------------------------------------- @@ -2238,11 +2619,34 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_MANUFACTURER_CODE); this.buffalo.writeUInt16(code); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Gets the manufacturer code to the specified value. + * The manufacturer code is one of the fields of the node descriptor. + * @returns The manufacturer code for the local node. + */ + async ezspGetManufacturerCode(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_MANUFACTURER_CODE); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const code = this.buffalo.readUInt16(); + + return code; } /** @@ -2251,14 +2655,22 @@ export class Ezsp extends EventEmitter { * changes. * @param descriptor uint16_t The new power descriptor for the local node. */ - async ezspSetPowerDescriptor(descriptor: number): Promise { + async ezspSetPowerDescriptor(descriptor: number): Promise { this.startCommand(EzspFrameID.SET_POWER_DESCRIPTOR); this.buffalo.writeUInt16(descriptor); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); + } + + if (this.version < 0x0E) { + return SLStatus.OK; + } else { + const status = this.buffalo.readUInt32(); + + return status; } } @@ -2272,17 +2684,17 @@ export class Ezsp extends EventEmitter { * initialization, EMBER_NOT_JOINED if the node is not part of a network, or the * reason for failure. */ - async ezspNetworkInit(networkInitStruct: EmberNetworkInitStruct): Promise { + async ezspNetworkInit(networkInitStruct: EmberNetworkInitStruct): Promise { this.startCommand(EzspFrameID.NETWORK_INIT); this.buffalo.writeEmberNetworkInitStruct(networkInitStruct); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2295,13 +2707,13 @@ export class Ezsp extends EventEmitter { async ezspNetworkState(): Promise { this.startCommand(EzspFrameID.NETWORK_STATE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberNetworkStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readUInt8(); return status; } @@ -2315,8 +2727,8 @@ export class Ezsp extends EventEmitter { * updated. * @param status Stack status */ - ezspStackStatusHandler(status: EmberStatus): void { - logger.debug(`ezspStackStatusHandler(): callback called with: [status=${EmberStatus[status]}]`, NS); + ezspStackStatusHandler(status: SLStatus): void { + logger.debug(`ezspStackStatusHandler(): callback called with: [status=${SLStatus[status]}]`, NS); this.emit(EzspEvents.STACK_STATUS, status); } @@ -2346,10 +2758,10 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt32(channelMask); this.buffalo.writeUInt8(duration); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const status: SLStatus = this.buffalo.readUInt32(); @@ -2390,8 +2802,8 @@ export class Ezsp extends EventEmitter { * EZSP_ACTIVE_SCAN. EMBER_SUCCESS signals that the scan has completed. Other * error conditions signify a failure to scan on the channel specified. */ - ezspScanCompleteHandler(channel: number, status: EmberStatus): void { - logger.debug(`ezspScanCompleteHandler(): callback called with: [channel=${channel}], [status=${EmberStatus[status]}]`, NS); + ezspScanCompleteHandler(channel: number, status: SLStatus): void { + logger.debug(`ezspScanCompleteHandler(): callback called with: [channel=${channel}], [status=${SLStatus[status]}]`, NS); } /** @@ -2401,7 +2813,7 @@ export class Ezsp extends EventEmitter { * @param The unused panID which has been found. * @param channel uint8_t The channel that the unused panID was found on. */ - ezspUnusedPanIdFoundHandler(panId: EmberPanId, channel: number): void { + ezspUnusedPanIdFoundHandler(panId: PanId, channel: number): void { logger.debug(`ezspUnusedPanIdFoundHandler(): callback called with: [panId=${panId}], [channel=${channel}]`, NS); } @@ -2412,18 +2824,18 @@ export class Ezsp extends EventEmitter { * @returns The error condition that occurred during the scan. Value will be * EMBER_SUCCESS if there are no errors. */ - async ezspFindUnusedPanId(channelMask: number, duration: number): Promise { + async ezspFindUnusedPanId(channelMask: number, duration: number): Promise { this.startCommand(EzspFrameID.FIND_UNUSED_PAN_ID); this.buffalo.writeUInt32(channelMask); this.buffalo.writeUInt8(duration); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); return status; } @@ -2432,16 +2844,16 @@ export class Ezsp extends EventEmitter { * Terminates a scan in progress. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspStopScan(): Promise { + async ezspStopScan(): Promise { this.startCommand(EzspFrameID.STOP_SCAN); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2451,17 +2863,17 @@ export class Ezsp extends EventEmitter { * @param parameters EmberNetworkParameters * Specification of the new network. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspFormNetwork(parameters: EmberNetworkParameters): Promise { + async ezspFormNetwork(parameters: EmberNetworkParameters): Promise { this.startCommand(EzspFrameID.FORM_NETWORK); this.buffalo.writeEmberNetworkParameters(parameters); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2476,22 +2888,22 @@ export class Ezsp extends EventEmitter { * @param parameters EmberNetworkParameters * Specification of the network with which the node should associate. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspJoinNetwork(nodeType: EmberNodeType, parameters: EmberNetworkParameters): Promise { + async ezspJoinNetwork(nodeType: EmberNodeType, parameters: EmberNetworkParameters): Promise { this.startCommand(EzspFrameID.JOIN_NETWORK); this.buffalo.writeUInt8(nodeType); this.buffalo.writeEmberNetworkParameters(parameters); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); return status; } - + /** * Causes the stack to associate with the network using the specified network * parameters in the beacon parameter. It can take several seconds for the stack @@ -2510,44 +2922,49 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. */ async ezspJoinNetworkDirectly(localNodeType: EmberNodeType, beacon: EmberBeaconData, radioTxPower: number, clearBeaconsAfterNetworkUp: boolean) - : Promise { + : Promise { this.startCommand(EzspFrameID.JOIN_NETWORK_DIRECTLY); this.buffalo.writeUInt8(localNodeType); this.buffalo.writeEmberBeaconData(beacon); - this.buffalo.writeInt8(radioTxPower); + this.buffalo.writeUInt8(radioTxPower); this.buffalo.writeUInt8(clearBeaconsAfterNetworkUp ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } - + /** * Causes the stack to leave the current network. This generates a * stackStatusHandler callback to indicate that the network is down. The radio * will not be used until after sending a formNetwork or joinNetwork command. + * @param options This parameter gives options when leave network * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspLeaveNetwork(): Promise { + async ezspLeaveNetwork(options: EmberLeaveNetworkOption = EmberLeaveNetworkOption.WITH_NO_OPTION): Promise { this.startCommand(EzspFrameID.LEAVE_NETWORK); - - const sendStatus: EzspStatus = await this.sendCommand(); + + if (this.version >= 0x0E) { + this.buffalo.writeUInt8(options); + } + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); return status; } - + /** * The application may call this function when contact with the network has been * lost. The most common usage case is when an end device can no longer @@ -2565,20 +2982,30 @@ export class Ezsp extends EventEmitter { * If the Trust Center allows the rejoin then the current Network Key will be sent encrypted using the device's Link Key. * @param channelMask uint32_t A mask indicating the channels to be scanned. See emberStartScan for format details. * A value of 0 is reinterpreted as the mask for the current channel. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspFindAndRejoinNetwork(haveCurrentNetworkKey: boolean, channelMask: number): Promise { + * @param reason uint8_t A sl_zigbee_rejoin_reason_t variable which could be passed in if there is actually a reason for rejoin, + * or could be left at 0xFF + * @param nodeType uint8_t The rejoin could be triggered with a different nodeType. + * This value could be set to 0 or SL_ZIGBEE_DEVICE_TYPE_UNCHANGED if not needed. + * @returns An SLStatus value indicating success or the reason for failure. + */ + async ezspFindAndRejoinNetwork(haveCurrentNetworkKey: boolean, channelMask: number, reason: number = 0xFF, nodeType: number = 0) + : Promise { this.startCommand(EzspFrameID.FIND_AND_REJOIN_NETWORK); this.buffalo.writeUInt8(haveCurrentNetworkKey ? 1 : 0); this.buffalo.writeUInt32(channelMask); - - const sendStatus: EzspStatus = await this.sendCommand(); + + if (this.version >= 0x0E) { + this.buffalo.writeUInt8(reason); + this.buffalo.writeUInt8(nodeType); + } + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2590,17 +3017,17 @@ export class Ezsp extends EventEmitter { * Any other value enables joining for that number of seconds. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspPermitJoining(duration: number): Promise { + async ezspPermitJoining(duration: number): Promise { this.startCommand(EzspFrameID.PERMIT_JOINING); this.buffalo.writeUInt8(duration); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); return status; } @@ -2614,7 +3041,7 @@ export class Ezsp extends EventEmitter { * @param childEui64 The EUI64 of the child. * @param childType The node type of the child. */ - ezspChildJoinHandler(index: number, joining: boolean, childId: EmberNodeId, childEui64: EmberEUI64, childType: EmberNodeType): void { + ezspChildJoinHandler(index: number, joining: boolean, childId: NodeId, childEui64: EUI64, childType: EmberNodeType): void { logger.debug(`ezspChildJoinHandler(): callback called with: [index=${index}], [joining=${joining}], ` + `[childId=${childId}], [childEui64=${childEui64}], [childType=${childType}]`, NS); } @@ -2631,20 +3058,20 @@ export class Ezsp extends EventEmitter { * @param scanCount uint16_t The number of scans to be performed on each channel (1..8). * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspEnergyScanRequest(target: EmberNodeId, scanChannels: number, scanDuration: number, scanCount: number): Promise { + async ezspEnergyScanRequest(target: NodeId, scanChannels: number, scanDuration: number, scanCount: number): Promise { this.startCommand(EzspFrameID.ENERGY_SCAN_REQUEST); this.buffalo.writeUInt16(target); this.buffalo.writeUInt32(scanChannels); this.buffalo.writeUInt8(scanDuration); this.buffalo.writeUInt16(scanCount); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); return status; } @@ -2655,39 +3082,39 @@ export class Ezsp extends EventEmitter { * @returns EmberNodeType * An EmberNodeType value indicating the current node type. * @returns EmberNetworkParameters * The current network parameters. */ - async ezspGetNetworkParameters(): Promise<[EmberStatus, nodeType: EmberNodeType, parameters: EmberNetworkParameters]> { + async ezspGetNetworkParameters(): Promise<[SLStatus, nodeType: EmberNodeType, parameters: EmberNetworkParameters]> { this.startCommand(EzspFrameID.GET_NETWORK_PARAMETERS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const nodeType = this.buffalo.readUInt8(); const parameters = this.buffalo.readEmberNetworkParameters(); return [status, nodeType, parameters]; } - + /** * Returns the current radio parameters based on phy index. * @param phyIndex uint8_t Desired index of phy interface for radio parameters. * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberMultiPhyRadioParameters * The current radio parameters based on provided phy index. */ - async ezspGetRadioParameters(phyIndex: number): Promise<[EmberStatus, parameters: EmberMultiPhyRadioParameters]> { + async ezspGetRadioParameters(phyIndex: number): Promise<[SLStatus, parameters: EmberMultiPhyRadioParameters]> { this.startCommand(EzspFrameID.GET_RADIO_PARAMETERS); this.buffalo.writeUInt8(phyIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const parameters = this.buffalo.readEmberMultiPhyRadioParameters(); return [status, parameters]; @@ -2698,16 +3125,16 @@ export class Ezsp extends EventEmitter { * the local node. * @returns uint8_t The number of children the node currently has. * @returns The parent's EUI64. The value is undefined for nodes without parents (coordinators and nodes that are not joined to a network). - * @returns EmberNodeId * The parent's node ID. The value is undefined for nodes without parents + * @returns NodeId * The parent's node ID. The value is undefined for nodes without parents * (coordinators and nodes that are not joined to a network). */ - async ezspGetParentChildParameters(): Promise<[number, parentEui64: EmberEUI64, parentNodeId: EmberNodeId]> { + async ezspGetParentChildParameters(): Promise<[number, parentEui64: EUI64, parentNodeId: NodeId]> { this.startCommand(EzspFrameID.GET_PARENT_CHILD_PARAMETERS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const childCount = this.buffalo.readUInt8(); @@ -2717,23 +3144,293 @@ export class Ezsp extends EventEmitter { return [childCount, parentEui64, parentNodeId]; } + /** + * Return the number of router children that the node currently has. + * @returns The number of router children. + */ + async ezspRouterChildCount(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.ROUTER_CHILD_COUNT); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const routerChildCount = this.buffalo.readUInt8(); + + return routerChildCount; + } + + /** + * Return the maximum number of children for this node. + * The return value is undefined for nodes that are not joined to a network. + * @returns The maximum number of children. + */ + async ezspMaxChildCount(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MAX_CHILD_COUNT); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const maxChildCount = this.buffalo.readUInt8(); + + return maxChildCount; + } + + /** + * Return the maximum number of router children for this node. + * The return value is undefined for nodes that are not joined to a network. + * @returns The maximum number of router children. + */ + async ezspMaxRouterChildCount(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MAX_ROUTER_CHILD_COUNT); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const maxRouterChildCount = this.buffalo.readUInt8(); + + return maxRouterChildCount; + } + + /** + * + * @returns + */ + async ezspGetParentIncomingNwkFrameCounter(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_PARENT_INCOMING_NWK_FRAME_COUNTER); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const parentIncomingNwkFrameCounter = this.buffalo.readUInt32(); + + return parentIncomingNwkFrameCounter; + } + + /** + * + * @param value uint32_t + * @returns + */ + async ezspSetParentIncomingNwkFrameCounter(value: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_PARENT_INCOMING_NWK_FRAME_COUNTER); + this.buffalo.writeUInt32(value); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Return a bitmask indicating the stack's current tasks. + * The mask ::SL_ZIGBEE_HIGH_PRIORITY_TASKS defines which tasks are high priority. + * Devices should not sleep if any high priority tasks are active. + * Active tasks that are not high priority are waiting for messages to arrive from other devices. + * If there are active tasks, but no high priority ones, the device may sleep but should periodically wake up + * and call ::emberPollForData() in order to receive messages. + * Parents will hold messages for ::SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT milliseconds before discarding them. + * @returns A bitmask of the stack's active tasks. + */ + async ezspCurrentStackTasks(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CURRENT_STACK_TASKS); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const activeTasks = this.buffalo.readUInt16(); + + return activeTasks; + } + + /** + * Indicate whether the stack is currently in a state where there are no high-priority tasks, allowing the device to sleep. + * There may be tasks expecting incoming messages, in which case the device should periodically wake up + * and call ::emberPollForData() in order to receive messages. + * This function can only be called when the node type is ::SL_ZIGBEE_SLEEPY_END_DEVICE + * @returns True if the application may sleep but the stack may be expecting incoming messages. + */ + async ezspOkToNap(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.OK_TO_NAP); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const value = this.buffalo.readUInt8() !== 0; + + return value; + } + + /** + * Indicate whether the parent token has been set by association. + * @returns True if the parent token has been set. + */ + async ezspParentTokenSet(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.PARENT_TOKEN_SET); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const indicator = this.buffalo.readUInt8() !== 0; + + return indicator; + } + + /** + * Indicate whether the stack currently has any tasks pending. + * If no tasks are pending, ::emberTick() does not need to be called until the next time a stack API function is called. + * This function can only be called when the node type is ::SL_ZIGBEE_SLEEPY_END_DEVICE. + * @returns True if the application may sleep for as long as it wishes. + */ + async ezspOkToHibernate(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.OK_TO_HIBERNATE); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const indicator = this.buffalo.readUInt8() !== 0; + + return indicator; + } + + /** + * Indicate whether the stack is currently in a state that does not require the application to periodically poll. + * @returns True if the device may poll less frequently. + */ + async ezspOkToLongPoll(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.OK_TO_LONG_POLL); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const indicator = this.buffalo.readUInt8() !== 0; + + return indicator; + } + + /** + * Calling this function will render all other stack functions except ezspStackPowerUp() non-functional until the radio is powered back on. + */ + async ezspStackPowerDown(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.STACK_POWER_DOWN); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Initialize the radio. Typically called coming out of deep sleep. + * For non-sleepy devices, also turns the radio on and leaves it in RX mode. + */ + async ezspStackPowerUp(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.STACK_POWER_UP); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + /** * Returns information about a child of the local node. * @param uint8_t The index of the child of interest in the child table. Possible indexes range from zero to EMBER_CHILD_TABLE_SIZE. * @returns EMBER_SUCCESS if there is a child at index. EMBER_NOT_JOINED if there is no child at index. * @returns EmberChildData * The data of the child. */ - async ezspGetChildData(index: number): Promise<[EmberStatus, childData: EmberChildData]> { + async ezspGetChildData(index: number): Promise<[SLStatus, childData: EmberChildData]> { this.startCommand(EzspFrameID.GET_CHILD_DATA); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const childData = this.buffalo.readEmberChildData(); return [status, childData]; @@ -2745,57 +3442,104 @@ export class Ezsp extends EventEmitter { * @param childData EmberChildData * The data of the child. * @returns EMBER_SUCCESS if the child data is set successfully at index. EMBER_INDEX_OUT_OF_RANGE if provided index is out of range. */ - async ezspSetChildData(index: number, childData: EmberChildData): Promise { + async ezspSetChildData(index: number, childData: EmberChildData): Promise { this.startCommand(EzspFrameID.SET_CHILD_DATA); this.buffalo.writeUInt8(index); this.buffalo.writeEmberChildData(childData); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } - + /** * Convert a child index to a node ID * @param childIndex uint8_t The index of the child of interest in the child table. Possible indexes range from zero to EMBER_CHILD_TABLE_SIZE. * @returns The node ID of the child or EMBER_NULL_NODE_ID if there isn't a child at the childIndex specified */ - async ezspChildId(childIndex: number): Promise { + async ezspChildId(childIndex: number): Promise { this.startCommand(EzspFrameID.CHILD_ID); this.buffalo.writeUInt8(childIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const childId: EmberNodeId = this.buffalo.readUInt16(); + const childId: NodeId = this.buffalo.readUInt16(); return childId; } + /** + * Return radio power value of the child from the given childIndex + * @param childIndex uint8_t The index of the child of interest in the child table. + * Possible indexes range from zero to SL_ZIGBEE_CHILD_TABLE_SIZE. + * @returns The power of the child or maximum radio power, which is the power value provided by the user + * while forming/joining a network if there isn't a child at the childIndex specified + */ + async ezspChilPower(childIndex: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CHILD_POWER); + this.buffalo.writeUInt8(childIndex); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const childPower = this.buffalo.readInt8(); + + return childPower; + } + + /** + * Set the radio power value for a given child index. + * @param childIndex uint8_t + * @param newPower int8_t + */ + async ezspSetChildPower(childIndex: number, newPower: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_CHILD_POWER); + this.buffalo.writeUInt8(childIndex); + this.buffalo.writeInt8(newPower); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + /** * Convert a node ID to a child index * @param childId The node ID of the child * @returns uint8_t The child index or 0xFF if the node ID doesn't belong to a child */ - async ezspChildIndex(childId: EmberNodeId): Promise { + async ezspChildIndex(childId: NodeId): Promise { this.startCommand(EzspFrameID.CHILD_INDEX); this.buffalo.writeUInt16(childId); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - + const childIndex = this.buffalo.readUInt8(); return childIndex; @@ -2807,13 +3551,13 @@ export class Ezsp extends EventEmitter { */ async ezspGetSourceRouteTableTotalSize(): Promise { this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - + const sourceRouteTableTotalSize = this.buffalo.readUInt8(); return sourceRouteTableTotalSize; @@ -2826,10 +3570,10 @@ export class Ezsp extends EventEmitter { async ezspGetSourceRouteTableFilledSize(): Promise { this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_FILLED_SIZE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const sourceRouteTableFilledSize = this.buffalo.readUInt8(); @@ -2842,20 +3586,20 @@ export class Ezsp extends EventEmitter { * @param index uint8_t The index of the entry of interest in the source route table. * Possible indexes range from zero to SOURCE_ROUTE_TABLE_FILLED_SIZE. * @returns EMBER_SUCCESS if there is source route entry at index. EMBER_NOT_FOUND if there is no source route at index. - * @returns EmberNodeId * The node ID of the destination in that entry. + * @returns NodeId * The node ID of the destination in that entry. * @returns uint8_t * The closer node index for this source route table entry */ - async ezspGetSourceRouteTableEntry(index: number): Promise<[EmberStatus, destination: EmberNodeId, closerIndex: number]> { + async ezspGetSourceRouteTableEntry(index: number): Promise<[SLStatus, destination: NodeId, closerIndex: number]> { this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_ENTRY); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const destination = this.buffalo.readUInt16(); const closerIndex = this.buffalo.readUInt8(); @@ -2871,17 +3615,17 @@ export class Ezsp extends EventEmitter { * Returns EMBER_SUCCESS otherwise. * @returns EmberNeighborTableEntry * The contents of the neighbor table entry. */ - async ezspGetNeighbor(index: number): Promise<[EmberStatus, value: EmberNeighborTableEntry]> { + async ezspGetNeighbor(index: number): Promise<[SLStatus, value: EmberNeighborTableEntry]> { this.startCommand(EzspFrameID.GET_NEIGHBOR); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const value = this.buffalo.readEmberNeighborTableEntry(); return [status, value]; @@ -2896,17 +3640,17 @@ export class Ezsp extends EventEmitter { * @returns Return EMBER_NOT_FOUND if the node is not found in the neighbor or child table. Returns EMBER_SUCCESS otherwise * @returns uint32_t * Return the frame counter of the node from the neighbor or child table */ - async ezspGetNeighborFrameCounter(eui64: EmberEUI64): Promise<[EmberStatus, returnFrameCounter: number]> { + async ezspGetNeighborFrameCounter(eui64: EUI64): Promise<[SLStatus, returnFrameCounter: number]> { this.startCommand(EzspFrameID.GET_NEIGHBOR_FRAME_COUNTER); this.buffalo.writeIeeeAddr(eui64); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const returnFrameCounter = this.buffalo.readUInt32(); return [status, returnFrameCounter]; @@ -2920,18 +3664,18 @@ export class Ezsp extends EventEmitter { * - EMBER_NOT_FOUND if the node is not found in the neighbor or child table. * - EMBER_SUCCESS otherwise */ - async ezspSetNeighborFrameCounter(eui64: EmberEUI64, frameCounter: number): Promise { + async ezspSetNeighborFrameCounter(eui64: EUI64, frameCounter: number): Promise { this.startCommand(EzspFrameID.SET_NEIGHBOR_FRAME_COUNTER); this.buffalo.writeIeeeAddr(eui64); this.buffalo.writeUInt32(frameCounter); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2942,17 +3686,17 @@ export class Ezsp extends EventEmitter { * @param costThresh uint8_t The routing shortcut threshold to configure. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetRoutingShortcutThreshold(costThresh: number): Promise { + async ezspSetRoutingShortcutThreshold(costThresh: number): Promise { this.startCommand(EzspFrameID.SET_ROUTING_SHORTCUT_THRESHOLD); this.buffalo.writeUInt8(costThresh); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -2965,13 +3709,14 @@ export class Ezsp extends EventEmitter { async ezspGetRoutingShortcutThreshold(): Promise { this.startCommand(EzspFrameID.GET_ROUTING_SHORTCUT_THRESHOLD); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const routingShortcutThresh = this.buffalo.readUInt8(); + return routingShortcutThresh; } @@ -2982,13 +3727,14 @@ export class Ezsp extends EventEmitter { async ezspNeighborCount(): Promise { this.startCommand(EzspFrameID.NEIGHBOR_COUNT); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const value = this.buffalo.readUInt8(); + return value; } @@ -3001,17 +3747,17 @@ export class Ezsp extends EventEmitter { * - EMBER_SUCCESS otherwise. * @returns EmberRouteTableEntry * The contents of the route table entry. */ - async ezspGetRouteTableEntry(index: number): Promise<[EmberStatus, value: EmberRouteTableEntry]> { + async ezspGetRouteTableEntry(index: number): Promise<[SLStatus, value: EmberRouteTableEntry]> { this.startCommand(EzspFrameID.GET_ROUTE_TABLE_ENTRY); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const value = this.buffalo.readEmberRouteTableEntry(); return [status, value]; @@ -3028,17 +3774,18 @@ export class Ezsp extends EventEmitter { * @param power int8_t Desired radio output power, in dBm. * @returns An EmberStatus value indicating the success or failure of the command. */ - async ezspSetRadioPower(power: number): Promise { + async ezspSetRadioPower(power: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_POWER); this.buffalo.writeInt8(power); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3050,17 +3797,18 @@ export class Ezsp extends EventEmitter { * @param channel uint8_t Desired radio channel. * @returns An EmberStatus value indicating the success or failure of the command. */ - async ezspSetRadioChannel(channel: number): Promise { + async ezspSetRadioChannel(channel: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_CHANNEL); this.buffalo.writeUInt8(channel); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3071,10 +3819,10 @@ export class Ezsp extends EventEmitter { async ezspGetRadioChannel(): Promise { this.startCommand(EzspFrameID.GET_RADIO_CHANNEL); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const channel = this.buffalo.readUInt8(); @@ -3088,17 +3836,18 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating the success or failure of the * command. */ - async ezspSetRadioIeee802154CcaMode(ccaMode: number): Promise { + async ezspSetRadioIeee802154CcaMode(ccaMode: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_IEEE802154_CCA_MODE); this.buffalo.writeUInt8(ccaMode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3120,7 +3869,7 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. */ async ezspSetConcentrator(on: boolean, concentratorType: number, minTime: number, maxTime: number, routeErrorThreshold: number, - deliveryFailureThreshold: number, maxHops: number): Promise { + deliveryFailureThreshold: number, maxHops: number): Promise { this.startCommand(EzspFrameID.SET_CONCENTRATOR); this.buffalo.writeUInt8(on ? 1 : 0); this.buffalo.writeUInt16(concentratorType); @@ -3130,33 +3879,79 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(deliveryFailureThreshold); this.buffalo.writeUInt8(maxHops); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } + + async ezspConcentratorStartDiscovery(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CONCENTRATOR_START_DISCOVERY); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + async ezspConcentratorStopDiscovery(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CONCENTRATOR_STOP_DISCOVERY); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + async ezspConcentratorNoteRouteError(status: SLStatus, nodeId: NodeId): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CONCENTRATOR_NOTE_ROUTE_ERROR); + this.buffalo.writeUInt32(status); + this.buffalo.writeUInt16(nodeId); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + /** * Sets the error code that is sent back from a router with a broken route. * @param errorCode uint8_t Desired error code. * @returns An EmberStatus value indicating the success or failure of the * command. */ - async ezspSetBrokenRouteErrorCode(errorCode: number): Promise { + async ezspSetBrokenRouteErrorCode(errorCode: number): Promise { this.startCommand(EzspFrameID.SET_BROKEN_ROUTE_ERROR_CODE); this.buffalo.writeUInt8(errorCode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3171,7 +3966,7 @@ export class Ezsp extends EventEmitter { * @param bitmask Network configuration bitmask. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspMultiPhyStart(phyIndex: number, page: number, channel: number, power: number, bitmask: EmberMultiPhyNwkConfig): Promise { + async ezspMultiPhyStart(phyIndex: number, page: number, channel: number, power: number, bitmask: EmberMultiPhyNwkConfig): Promise { this.startCommand(EzspFrameID.MULTI_PHY_START); this.buffalo.writeUInt8(phyIndex); this.buffalo.writeUInt8(page); @@ -3179,13 +3974,14 @@ export class Ezsp extends EventEmitter { this.buffalo.writeInt8(power); this.buffalo.writeUInt8(bitmask); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3194,17 +3990,18 @@ export class Ezsp extends EventEmitter { * @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspMultiPhyStop(phyIndex: number): Promise { + async ezspMultiPhyStop(phyIndex: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_STOP); this.buffalo.writeUInt8(phyIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3221,35 +4018,37 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating the success or failure of the * command. */ - async ezspMultiPhySetRadioPower(phyIndex: number, power: number): Promise { + async ezspMultiPhySetRadioPower(phyIndex: number, power: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_SET_RADIO_POWER); this.buffalo.writeUInt8(phyIndex); this.buffalo.writeInt8(power); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } - + /** * Send Link Power Delta Request from a child to its parent * @returns An EmberStatus value indicating the success or failure of sending the request. */ - async ezspSendLinkPowerDeltaRequest(): Promise { + async ezspSendLinkPowerDeltaRequest(): Promise { this.startCommand(EzspFrameID.SEND_LINK_POWER_DELTA_REQUEST); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3264,19 +4063,20 @@ export class Ezsp extends EventEmitter { * @param channel uint8_t Desired radio channel. * @returns An EmberStatus value indicating the success or failure of the command. */ - async ezspMultiPhySetRadioChannel(phyIndex: number, page: number, channel: number): Promise { + async ezspMultiPhySetRadioChannel(phyIndex: number, page: number, channel: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_SET_RADIO_CHANNEL); this.buffalo.writeUInt8(phyIndex); this.buffalo.writeUInt8(page); this.buffalo.writeUInt8(channel); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3285,16 +4085,16 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating the success or failure of the command. * @returns EmberDutyCycleState * The current duty cycle state in effect. */ - async ezspGetDutyCycleState(): Promise<[EmberStatus, returnedState: EmberDutyCycleState]> { + async ezspGetDutyCycleState(): Promise<[SLStatus, returnedState: EmberDutyCycleState]> { this.startCommand(EzspFrameID.GET_DUTY_CYCLE_STATE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const returnedState = this.buffalo.readUInt8(); return [status, returnedState]; @@ -3309,17 +4109,18 @@ export class Ezsp extends EventEmitter { * one of the limits to default or violates constraints Susp > Crit > Limi, * EMBER_INVALID_CALL if device is operating on 2.4Ghz */ - async ezspSetDutyCycleLimitsInStack(limits: EmberDutyCycleLimits): Promise { + async ezspSetDutyCycleLimitsInStack(limits: EmberDutyCycleLimits): Promise { this.startCommand(EzspFrameID.SET_DUTY_CYCLE_LIMITS_IN_STACK); this.buffalo.writeEmberDutyCycleLimits(limits); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3330,16 +4131,16 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating the success or failure of the command. * @returns EmberDutyCycleLimits * Return current duty cycle limits if returnedLimits is not NULL */ - async ezspGetDutyCycleLimits(): Promise<[EmberStatus, returnedLimits: EmberDutyCycleLimits]> { + async ezspGetDutyCycleLimits(): Promise<[SLStatus, returnedLimits: EmberDutyCycleLimits]> { this.startCommand(EzspFrameID.GET_DUTY_CYCLE_LIMITS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const returnedLimits = this.buffalo.readEmberDutyCycleLimits(); return [status, returnedLimits]; @@ -3356,19 +4157,19 @@ export class Ezsp extends EventEmitter { * - EMBER_SUCCESS if the duty cycles were read successfully, * - EMBER_BAD_ARGUMENT maxDevices is greater than EMBER_MAX_END_DEVICE_CHILDREN + 1. * @returns uint8_t * Consumed duty cycles up to maxDevices. When the number of children that are being monitored is less than maxDevices, - * the EmberNodeId element in the EmberPerDeviceDutyCycle will be 0xFFFF. + * the NodeId element in the EmberPerDeviceDutyCycle will be 0xFFFF. */ - async ezspGetCurrentDutyCycle(maxDevices: number): Promise<[EmberStatus, arrayOfDeviceDutyCycles: number[]]> { + async ezspGetCurrentDutyCycle(maxDevices: number): Promise<[SLStatus, arrayOfDeviceDutyCycles: number[]]> { this.startCommand(EzspFrameID.GET_CURRENT_DUTY_CYCLE); this.buffalo.writeUInt8(maxDevices); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const arrayOfDeviceDutyCycles = this.buffalo.readListUInt8(134); return [status, arrayOfDeviceDutyCycles]; @@ -3391,82 +4192,94 @@ export class Ezsp extends EventEmitter { } /** - * Returns the first beacon in the cache. Beacons are stored in cache after - * issuing an active scan. + * Configure the number of beacons to store when issuing active scans for networks. + * @param numBeacons uint8_t The number of beacons to cache when scanning. * @returns - * - EMBER_SUCCESS if first beacon found, - * - EMBER_BAD_ARGUMENT if input parameters are invalid, EMBER_INVALID_CALL if no beacons stored, - * - EMBER_ERR_FATAL if no first beacon found. - * @returns EmberBeaconIterator * The iterator to use when returning the first beacon. This argument must not be NULL. + * - SL_STATUS_INVALID_PARAMETER if numBeacons is greater than + * - SL_ZIGBEE_MAX_BEACONS_TO_STORE + * - SL_STATUS_OK */ - async ezspGetFirstBeacon(): Promise<[EmberStatus, beaconIterator: EmberBeaconIterator]> { - this.startCommand(EzspFrameID.GET_FIRST_BEACON); - - const sendStatus: EzspStatus = await this.sendCommand(); + async ezspSetNumBeaconToStore(numBeacons: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_NUM_BEACONS_TO_STORE); + this.buffalo.writeUInt8(numBeacons); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); - const beaconIterator = this.buffalo.readEmberBeaconIterator(); - return [status, beaconIterator]; - } - + const status = this.buffalo.readUInt32(); + + return status; + } + /** - * Returns the next beacon in the cache. Beacons are stored in cache after - * issuing an active scan. - * @returns - * - EMBER_SUCCESS if next beacon found, - * - EMBER_BAD_ARGUMENT if input parameters are invalid, - * - EMBER_ERR_FATAL if no next beacon found. - * @returns EmberBeaconData * The next beacon retrieved. It is assumed that emberGetFirstBeacon has been called first. - * This argument must not be NULL. + * Fetches the specified beacon in the cache. Beacons are stored in cache after issuing an active scan. + * @param beaconNumber uint8_t The beacon index to fetch. Valid values range from 0 to ezspGetNumStoredBeacons-1. + * @returns An appropriate sl_status_t status code. + * @returns EmberBeaconData * The beacon to populate upon success. */ - async ezspGetNextBeacon(): Promise<[EmberStatus, beacon: EmberBeaconData]> { - this.startCommand(EzspFrameID.GET_NEXT_BEACON); - - const sendStatus: EzspStatus = await this.sendCommand(); + async ezspGetStoredBeacon(beaconNumber: number): Promise<[SLStatus, beacon: EmberBeaconData]> { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_STORED_BEACON); + this.buffalo.writeUInt8(beaconNumber); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readUInt32(); const beacon = this.buffalo.readEmberBeaconData(); return [status, beacon]; } - + /** * Returns the number of cached beacons that have been collected from a scan. * @returns uint8_t The number of cached beacons that have been collected from a scan. */ async ezspGetNumStoredBeacons(): Promise { this.startCommand(EzspFrameID.GET_NUM_STORED_BEACONS); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - + const numBeacons = this.buffalo.readUInt8(); return numBeacons; } - + /** * Clears all cached beacons that have been collected from a scan. */ - async ezspClearStoredBeacons(): Promise { + async ezspClearStoredBeacons(): Promise { this.startCommand(EzspFrameID.CLEAR_STORED_BEACONS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); + } + + if (this.version < 0x0E) { + return SLStatus.OK; + } else { + const status = this.buffalo.readUInt32(); + + return status; } } @@ -3476,20 +4289,227 @@ export class Ezsp extends EventEmitter { * @param radioChannel uint8_t The radio channel to be set. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetLogicalAndRadioChannel(radioChannel: number): Promise { + async ezspSetLogicalAndRadioChannel(radioChannel: number): Promise { this.startCommand(EzspFrameID.SET_LOGICAL_AND_RADIO_CHANNEL); this.buffalo.writeUInt8(radioChannel); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } - + + /** + * Form a new sleepy-to-sleepy network. + * If the network is using security, the device must call sli_zigbee_stack_set_initial_security_state() first. + * @param parameters Specification of the new network. + * @param initiator Whether this device is initiating or joining the network. + * @returns An sl_status_t value indicating success or a reason for failure. + */ + async ezspSleepyToSleepyNetworkStart(parameters: EmberNetworkParameters, initiator: boolean): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SLEEPY_TO_SLEEPY_NETWORK_START); + this.buffalo.writeEmberNetworkParameters(parameters); + this.buffalo.writeUInt8(initiator ? 1 : 0); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Send a Zigbee NWK Leave command to the destination. + * @param destination Node ID of the device being told to leave. + * @param flags Bitmask indicating additional considerations for the leave request. + * @returns Status indicating success or a reason for failure. Call is invalid if destination is on network or is the local node. + */ + async ezspSendZigbeeLeave(destination: PanId, flags: Zdo.LeaveRequestFlags): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SEND_ZIGBEE_LEAVE); + this.buffalo.writeUInt16(destination); + this.buffalo.writeUInt8(flags); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Indicate the state of permit joining in MAC. + * @returns Whether the current network permits joining. + */ + async ezspGetPermitJoining(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_PERMIT_JOINING); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const joiningPermitted = this.buffalo.readUInt8() !== 0; + + return joiningPermitted; + } + + /** + * Get the 8-byte extended PAN ID of this node. + * @returns Extended PAN ID of this node. Valid only if it is currently on a network. + */ + async ezspGetExtendedPanId(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_EXTENDED_PAN_ID); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const extendedPanId = this.buffalo.readListUInt8(ZSpec.EXTENDED_PAN_ID_SIZE); + + return extendedPanId; + } + + /** + * Get the current network. + * @returns Return the current network index. + */ + async ezspGetCurrentNetwork(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_CURRENT_NETWORK); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const index = this.buffalo.readUInt8(); + + return index; + } + + /** + * Set initial outgoing link cost for neighbor. + * @param cost The new default cost. Valid values are 0, 1, 3, 5, and 7. + * @returns Whether or not initial cost was successfully set. + */ + async ezspSetInitialNeighborOutgoingCost(cost: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_INITIAL_NEIGHBOR_OUTGOING_COST); + this.buffalo.writeUInt8(cost); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Get initial outgoing link cost for neighbor. + * @returns The default cost associated with new neighbor's outgoing links. + */ + async ezspGetInitialNeighborOutgoingCost(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_INITIAL_NEIGHBOR_OUTGOING_COST); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const cost = this.buffalo.readUInt8(); + + return cost; + } + + /** + * Indicate whether a rejoining neighbor should have its incoming frame counter reset. + * @param reset + */ + async ezspResetRejoiningNeighborsFrameCounter(reset: boolean): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RESET_REJOINING_NEIGHBORS_FRAME_COUNTER); + this.buffalo.writeUInt8(reset ? 1 : 0); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Check whether a rejoining neighbor will have its incoming frame counter reset based on the currently set policy. + * @returns Whether or not a rejoining neighbor's incoming FC gets reset (true or false). + */ + async ezspIsResetRejoiningNeighborsFrameCounterEnabled(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.IS_RESET_REJOINING_NEIGHBORS_FRAME_COUNTER_ENABLED); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const getsReset = this.buffalo.readUInt8() !== 0; + + return getsReset; + } + //----------------------------------------------------------------------------- // Binding Frames //----------------------------------------------------------------------------- @@ -3498,16 +4518,17 @@ export class Ezsp extends EventEmitter { * Deletes all binding table entries. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspClearBindingTable(): Promise { + async ezspClearBindingTable(): Promise { this.startCommand(EzspFrameID.CLEAR_BINDING_TABLE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3517,18 +4538,19 @@ export class Ezsp extends EventEmitter { * @param value EmberBindingTableEntry * The contents of the binding entry. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetBinding(index: number, value: EmberBindingTableEntry): Promise { + async ezspSetBinding(index: number, value: EmberBindingTableEntry): Promise { this.startCommand(EzspFrameID.SET_BINDING); this.buffalo.writeUInt8(index); this.buffalo.writeEmberBindingTableEntry(value); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3538,17 +4560,17 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberBindingTableEntry * The contents of the binding entry. */ - async ezspGetBinding(index: number): Promise<[EmberStatus, value: EmberBindingTableEntry]> { + async ezspGetBinding(index: number): Promise<[SLStatus, value: EmberBindingTableEntry]> { this.startCommand(EzspFrameID.GET_BINDING); this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const value = this.buffalo.readEmberBindingTableEntry(); return [status, value]; @@ -3559,17 +4581,18 @@ export class Ezsp extends EventEmitter { * @param index uint8_t The index of a binding table entry. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspDeleteBinding(index: number): Promise { + async ezspDeleteBinding(index: number): Promise { this.startCommand(EzspFrameID.DELETE_BINDING); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -3585,13 +4608,13 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.BINDING_IS_ACTIVE); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const active = this.buffalo.readUInt8() === 1 ? true : false; + const active = this.buffalo.readUInt8() !== 0; return active; } @@ -3608,17 +4631,17 @@ export class Ezsp extends EventEmitter { * @param index uint8_t The index of a binding table entry. * @returns The short ID of the destination node or EMBER_NULL_NODE_ID if no destination is known. */ - async ezspGetBindingRemoteNodeId(index: number): Promise { + async ezspGetBindingRemoteNodeId(index: number): Promise { this.startCommand(EzspFrameID.GET_BINDING_REMOTE_NODE_ID); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const nodeId: EmberNodeId = this.buffalo.readUInt16(); + const nodeId: NodeId = this.buffalo.readUInt16(); return nodeId; } @@ -3629,15 +4652,15 @@ export class Ezsp extends EventEmitter { * @param index uint8_t The index of a binding table entry. * @param The short ID of the destination node. */ - async ezspSetBindingRemoteNodeId(index: number, nodeId: EmberNodeId): Promise { + async ezspSetBindingRemoteNodeId(index: number, nodeId: NodeId): Promise { this.startCommand(EzspFrameID.SET_BINDING_REMOTE_NODE_ID); this.buffalo.writeUInt8(index); this.buffalo.writeUInt16(nodeId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -3649,11 +4672,11 @@ export class Ezsp extends EventEmitter { * command. * @param entry EmberBindingTableEntry * The requested binding. * @param index uint8_t The index at which the binding was added. - * @param policyDecision EMBER_SUCCESS if the binding was added to the table and any other status if not. + * @param policyDecision SL_STATUS_OK if the binding was added to the table and any other status if not. */ - ezspRemoteSetBindingHandler(entry: EmberBindingTableEntry, index: number, policyDecision: EmberStatus): void { + ezspRemoteSetBindingHandler(entry: EmberBindingTableEntry, index: number, policyDecision: SLStatus): void { logger.debug(`ezspRemoteSetBindingHandler(): callback called with: [entry=${entry}], [index=${index}], ` - + `[policyDecision=${EmberStatus[policyDecision]}]`, NS); + + `[policyDecision=${SLStatus[policyDecision]}]`, NS); } /** @@ -3663,10 +4686,10 @@ export class Ezsp extends EventEmitter { * but it can change the policy for future decisions using the setPolicy * command. * @param index uint8_t The index of the binding whose deletion was requested. - * @param policyDecision EMBER_SUCCESS if the binding was removed from the table and any other status if not. + * @param policyDecision SL_STATUS_OK if the binding was removed from the table and any other status if not. */ - ezspRemoteDeleteBindingHandler(index: number, policyDecision: EmberStatus): void { - logger.debug(`ezspRemoteDeleteBindingHandler(): callback called with: [index=${index}], [policyDecision=${EmberStatus[policyDecision]}]`, NS); + ezspRemoteDeleteBindingHandler(index: number, policyDecision: SLStatus): void { + logger.debug(`ezspRemoteDeleteBindingHandler(): callback called with: [index=${index}], [policyDecision=${SLStatus[policyDecision]}]`, NS); } //----------------------------------------------------------------------------- @@ -3680,10 +4703,10 @@ export class Ezsp extends EventEmitter { async ezspMaximumPayloadLength(): Promise { this.startCommand(EzspFrameID.MAXIMUM_PAYLOAD_LENGTH); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const apsLength = this.buffalo.readUInt8(); @@ -3692,51 +4715,54 @@ export class Ezsp extends EventEmitter { } /** - * Sends a unicast message as per the ZigBee specification. The message will - * arrive at its destination only if there is a known route to the destination - * node. Setting the ENABLE_ROUTE_DISCOVERY option will cause a route to be - * discovered if none is known. Setting the FORCE_ROUTE_DISCOVERY option will - * force route discovery. Routes to end-device children of the local node are - * always known. Setting the APS_RETRY option will cause the message to be - * retransmitted until either a matching acknowledgement is received or three - * transmissions have been made. Note: Using the FORCE_ROUTE_DISCOVERY option - * will cause the first transmission to be consumed by a route request as part - * of discovery, so the application payload of this packet will not reach its - * destination on the first attempt. If you want the packet to reach its - * destination, the APS_RETRY option must be set so that another attempt is made - * to transmit the message with its application payload after the route has been - * constructed. Note: When sending fragmented messages, the stack will only - * assign a new APS sequence number for the first fragment of the message (i.e., - * EMBER_APS_OPTION_FRAGMENT is set and the low-order byte of the groupId field - * in the APS frame is zero). For all subsequent fragments of the same message, - * the application must set the sequence number field in the APS frame to the - * sequence number assigned by the stack to the first fragment. + * Sends a unicast message as per the ZigBee specification. + * The message will arrive at its destination only if there is a known route to the destination node. + * Setting the ENABLE_ROUTE_DISCOVERY option will cause a route to be discovered if none is known. + * Setting the FORCE_ROUTE_DISCOVERY option will force route discovery. + * Routes to end-device children of the local node are always known. + * Setting the APS_RETRY option will cause the message to be retransmitted until either a matching acknowledgement is received + * or three transmissions have been made. + * Note: Using the FORCE_ROUTE_DISCOVERY option will cause the first transmission to be consumed by a route request as part of discovery, + * so the application payload of this packet will not reach its destination on the first attempt. If you want the packet to reach its destination, + * the APS_RETRY option must be set so that another attempt is made to transmit the message with its application payload + * after the route has been constructed. + * Note: When sending fragmented messages, the stack will only assign a new APS sequence number for the first fragment of the message + * (i.e., SL_ZIGBEE_APS_OPTION_FRAGMENT is set and the low-order byte of the groupId field in the APS frame is zero). + * For all subsequent fragments of the same message, the application must set the sequence number field in the APS frame + * to the sequence number assigned by the stack to the first fragment. * @param type Specifies the outgoing message type. * Must be one of EMBER_OUTGOING_DIRECT, EMBER_OUTGOING_VIA_ADDRESS_TABLE, or EMBER_OUTGOING_VIA_BINDING. - * @param indexOrDestination Depending on the type of addressing used, this is either the EmberNodeId of the destination, + * @param indexOrDestination Depending on the type of addressing used, this is either the NodeId of the destination, * an index into the address table, or an index into the binding table. * @param apsFrame EmberApsFrame * The APS frame which is to be added to the message. - * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. + * @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host. + * This value is used in the ezspMessageSentHandler response to refer to this message. * @param messageContents uint8_t * Content of the message. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An sl_status_t value indicating success or the reason for failure. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ - async ezspSendUnicast(type: EmberOutgoingMessageType, indexOrDestination: EmberNodeId, apsFrame: EmberApsFrame, messageTag: number, - messageContents: Buffer): Promise<[EmberStatus, apsSequence: number]> { + async ezspSendUnicast(type: EmberOutgoingMessageType, indexOrDestination: NodeId, apsFrame: EmberApsFrame, messageTag: number, + messageContents: Buffer): Promise<[SLStatus, apsSequence: number]> { this.startCommand(EzspFrameID.SEND_UNICAST); this.buffalo.writeUInt8(type); this.buffalo.writeUInt16(indexOrDestination); this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(messageTag); + + if (this.version < 0x0E) { + this.buffalo.writeUInt8(messageTag); + } else { + this.buffalo.writeUInt16(messageTag); + } + this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const apsSequence = this.buffalo.readUInt8(); return [status, apsSequence]; @@ -3744,154 +4770,122 @@ export class Ezsp extends EventEmitter { /** * Sends a broadcast message as per the ZigBee specification. + * @param alias The aliased source from which we send the broadcast. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source * @param destination The destination to which to send the broadcast. This must be one of the three ZigBee broadcast addresses. + * @param nwkSequence The alias nwk sequence number. This won't be used if there is no aliased source. * @param apsFrame EmberApsFrame * The APS frame for the message. - * @param radius uint8_t The message will be delivered to all nodes within radius hops of the sender. + * @param radius uint8_t (v14+: uint16_t) The message will be delivered to all nodes within radius hops of the sender. * A radius of zero is converted to EMBER_MAX_HOPS. * @param uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. * @param uint8_t * The broadcast message. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An sl_status_t value indicating success or the reason for failure. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ - async ezspSendBroadcast(destination: EmberNodeId, apsFrame: EmberApsFrame, radius: number, messageTag: number, messageContents: Buffer): - Promise<[EmberStatus, apsSequence: number]> { + async ezspSendBroadcast(alias: NodeId, destination: NodeId, nwkSequence: number, apsFrame: EmberApsFrame, radius: number, messageTag: number, + messageContents: Buffer): Promise<[SLStatus, apsSequence: number]> { this.startCommand(EzspFrameID.SEND_BROADCAST); - this.buffalo.writeUInt16(destination); - this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(radius); - this.buffalo.writeUInt8(messageTag); + + if (this.version < 0x0E) { + this.buffalo.writeUInt16(destination); + this.buffalo.writeEmberApsFrame(apsFrame); + this.buffalo.writeUInt8(radius); + this.buffalo.writeUInt8(messageTag); + } else { + this.buffalo.writeUInt16(alias); + this.buffalo.writeUInt16(destination); + this.buffalo.writeUInt8(nwkSequence); + this.buffalo.writeEmberApsFrame(apsFrame); + this.buffalo.writeUInt8(radius); + this.buffalo.writeUInt16(messageTag); + } + this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const apsSequence = this.buffalo.readUInt8(); return [status, apsSequence]; } /** - * Sends a proxied broadcast message as per the ZigBee specification. - * @param source The source from which to send the broadcast. - * @param destination The destination to which to send the broadcast. This must be one of the three ZigBee broadcast addresses. - * @param nwkSequence uint8_t The network sequence number for the broadcast. - * @param apsFrame EmberApsFrame * The APS frame for the message. - * @param radius uint8_t The message will be delivered to all nodes within radius hops of the sender. - * A radius of zero is converted to EMBER_MAX_HOPS. - * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. - * @param messageContents uint8_t * The broadcast message. - * @returns An EmberStatus value indicating success or the reason for failure. - * @returns uint8_t * The APS sequence number that will be used when this message is transmitted. + * Sends proxied broadcast message for another node in conjunction with sl_zigbee_proxy_broadcast + * where a long source is also specified in the NWK frame control. + * @param euiSource The long source from which to send the broadcast + * @returns An sl_status_t value indicating success or the reason for failure. */ - async ezspProxyBroadcast(source: EmberNodeId, destination: EmberNodeId, nwkSequence: number, apsFrame: EmberApsFrame, radius: number, - messageTag: number, messageContents: Buffer): Promise<[EmberStatus, apsSequence: number]> { - this.startCommand(EzspFrameID.PROXY_BROADCAST); - this.buffalo.writeUInt16(source); - this.buffalo.writeUInt16(destination); - this.buffalo.writeUInt8(nwkSequence); - this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(radius); - this.buffalo.writeUInt8(messageTag); - this.buffalo.writePayload(messageContents); + async ezspProxyNextBroadcastFromLong(euiSource: EUI64): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.PROXY_NEXT_BROADCAST_FROM_LONG); + this.buffalo.writeIeeeAddr(euiSource); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); - const apsSequence = this.buffalo.readUInt8(); - return [status, apsSequence]; + const status = this.buffalo.readUInt32(); + + return status; } /** - * Sends a multicast message to all endpoints that share a specific multicast ID - * and are within a specified number of hops of the sender. + * Sends a multicast message to all endpoints that share a specific multicast ID and are within a specified number of hops of the sender. * @param apsFrame EmberApsFrame * The APS frame for the message. The multicast will be sent to the groupId in this frame. * @param hops uint8_t The message will be delivered to all nodes within this number of hops of the sender. * A value of zero is converted to EMBER_MAX_HOPS. - * @param nonmemberRadius uint8_t The number of hops that the message will be forwarded by devices that are not members of the group. + * @param broadcastAddr The number of hops that the message will be forwarded by devices that are not members of the group. * A value of 7 or greater is treated as infinite. + * @param alias The alias source address. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source + * @param nwkSequence The alias sequence number. This won't be used if there is no aliased source. * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. + * @param messageLength uint8_t (v14+: uint16_t) The length of the messageContents parameter in bytes. * @param messageContents uint8_t * The multicast message. - * @returns An EmberStatus value. For any result other than EMBER_SUCCESS, the message will not be sent. - * - EMBER_SUCCESS - The message has been submitted for transmission. - * - EMBER_INVALID_BINDING_INDEX - The bindingTableIndex refers to a non-multicast binding. - * - EMBER_NETWORK_DOWN - The node is not part of a network. - * - EMBER_MESSAGE_TOO_LONG - The message is too large to fit in a MAC layer frame. - * - EMBER_NO_BUFFERS - The free packet buffer pool is empty. - * - EMBER_NETWORK_BUSY - Insufficient resources available in Network or MAC layers to send message. + * @returns An sl_status_t value. For any result other than SL_STATUS_OK, the message will not be sent. + * - SL_STATUS_OK - The message has been submitted for transmission. + * - SL_STATUS_INVALID_INDEX - The bindingTableIndex refers to a non-multicast binding. + * - SL_STATUS_NETWORK_DOWN - The node is not part of a network. + * - SL_STATUS_MESSAGE_TOO_LONG - The message is too large to fit in a MAC layer frame. + * - SL_STATUS_ALLOCATION_FAILED - The free packet buffer pool is empty. + * - SL_STATUS_BUSY - Insufficient resources available in Network or MAC layers to send message. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ - async ezspSendMulticast(apsFrame: EmberApsFrame, hops: number, nonmemberRadius: number, messageTag: number, messageContents: Buffer): - Promise<[EmberStatus, apsSequence: number]> { + async ezspSendMulticast(apsFrame: EmberApsFrame, hops: number, broadcastAddr: number, alias: NodeId, nwkSequence: number, messageTag: number, + messageContents: Buffer): Promise<[SLStatus, apsSequence: number]> { this.startCommand(EzspFrameID.SEND_MULTICAST); this.buffalo.writeEmberApsFrame(apsFrame); this.buffalo.writeUInt8(hops); - this.buffalo.writeUInt8(nonmemberRadius); - this.buffalo.writeUInt8(messageTag); - this.buffalo.writePayload(messageContents); - - const sendStatus: EzspStatus = await this.sendCommand(); - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + if (this.version < 0x0E) { + this.buffalo.writeUInt8(ZA_MAX_HOPS);// nonMemberRadius + this.buffalo.writeUInt8(messageTag); + } else { + this.buffalo.writeUInt16(broadcastAddr); + this.buffalo.writeUInt16(alias); + this.buffalo.writeUInt8(nwkSequence); + this.buffalo.writeUInt16(messageTag); } - - const status: EmberStatus = this.buffalo.readUInt8(); - const apsSequence = this.buffalo.readUInt8(); - return [status, apsSequence]; - } - /** - * Sends a multicast message to all endpoints that share a specific multicast ID - * and are within a specified number of hops of the sender. - * @param apsFrame EmberApsFrame * The APS frame for the message. The multicast will be sent to the groupId in this frame. - * @param hops uint8_t The message will be delivered to all nodes within this number of hops of the sender. - * A value of zero is converted to EMBER_MAX_HOPS. - * @param nonmemberRadius uint8_t The number of hops that the message will be forwarded by devices that are not members of the group. - * A value of 7 or greater is treated as infinite. - * @param alias uint16_t The alias source address - * @param nwkSequence uint8_t the alias sequence number - * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. - * @param messageContents uint8_t * The multicast message. - * @returns An EmberStatus value. For any result other than EMBER_SUCCESS, the - * message will not be sent. EMBER_SUCCESS - The message has been submitted for - * transmission. EMBER_INVALID_BINDING_INDEX - The bindingTableIndex refers to a - * non-multicast binding. EMBER_NETWORK_DOWN - The node is not part of a - * network. EMBER_MESSAGE_TOO_LONG - The message is too large to fit in a MAC - * layer frame. EMBER_NO_BUFFERS - The free packet buffer pool is empty. - * EMBER_NETWORK_BUSY - Insufficient resources available in Network or MAC - * layers to send message. - * @returns The sequence number that will be used when this message is transmitted. - */ - async ezspSendMulticastWithAlias(apsFrame: EmberApsFrame, hops: number, nonmemberRadius: number, alias: number, nwkSequence: number, - messageTag: number, messageContents: Buffer): Promise<[EmberStatus, apsSequence: number]> { - this.startCommand(EzspFrameID.SEND_MULTICAST_WITH_ALIAS); - this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(hops); - this.buffalo.writeUInt8(nonmemberRadius); - this.buffalo.writeUInt16(alias); - this.buffalo.writeUInt8(nwkSequence); - this.buffalo.writeUInt8(messageTag); this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const apsSequence = this.buffalo.readUInt8(); + return [status, apsSequence]; } @@ -3916,46 +4910,47 @@ export class Ezsp extends EventEmitter { * - EMBER_NETWORK_BUSY - Either no route or insufficient resources available. * - EMBER_SUCCESS - The reply was successfully queued for transmission. */ - async ezspSendReply(sender: EmberNodeId, apsFrame: EmberApsFrame, messageContents: Buffer): - Promise { + async ezspSendReply(sender: NodeId, apsFrame: EmberApsFrame, messageContents: Buffer): + Promise { this.startCommand(EzspFrameID.SEND_REPLY); this.buffalo.writeUInt16(sender); this.buffalo.writeEmberApsFrame(apsFrame); this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } /** * Callback * A callback indicating the stack has completed sending a message. + * @param status An EmberStatus value of EMBER_SUCCESS if an ACK was received from the destination + * or EMBER_DELIVERY_FAILED if no ACK was received. * @param type The type of message sent. * @param indexOrDestination uint16_t The destination to which the message was sent, for direct unicasts, * or the address table or binding index for other unicasts. The value is unspecified for multicasts and broadcasts. * @param apsFrame EmberApsFrame * The APS frame for the message. * @param messageTag uint8_t The value supplied by the Host in the ezspSendUnicast, ezspSendBroadcast or ezspSendMulticast command. - * @param status An EmberStatus value of EMBER_SUCCESS if an ACK was received from the destination - * or EMBER_DELIVERY_FAILED if no ACK was received. * @param messageContents uint8_t * The unicast message supplied by the Host. The message contents are only included here if the decision * for the messageContentsInCallback policy is messageTagAndContentsInCallback. */ - ezspMessageSentHandler(type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number, - status: EmberStatus, messageContents: Buffer): void { + ezspMessageSentHandler(status: SLStatus, type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number, + messageContents: Buffer): void { logger.debug( - `ezspMessageSentHandler(): callback called with: [type=${EmberOutgoingMessageType[type]}], [indexOrDestination=${indexOrDestination}], ` - + `[apsFrame=${JSON.stringify(apsFrame)}], [messageTag=${messageTag}], [status=${EmberStatus[status]}], ` + `ezspMessageSentHandler(): callback called with: [status=${SLStatus[status]}], [type=${EmberOutgoingMessageType[type]}], ` + + `[indexOrDestination=${indexOrDestination}], [apsFrame=${JSON.stringify(apsFrame)}], [messageTag=${messageTag}], ` + `[messageContents=${messageContents.toString('hex')}]`, NS, ); - this.emit(EzspEvents.MESSAGE_SENT, type, indexOrDestination, apsFrame, messageTag, status); + this.emit(EzspEvents.MESSAGE_SENT, status, type, indexOrDestination, apsFrame, messageTag); } /** @@ -3994,81 +4989,209 @@ export class Ezsp extends EventEmitter { * @returns EMBER_SUCCESS if the route request was successfully submitted to the * transmit queue, and EMBER_ERR_FATAL otherwise. */ - async ezspSendManyToOneRouteRequest(concentratorType: number, radius: number): Promise { - this.startCommand(EzspFrameID.SEND_MANY_TO_ONE_ROUTE_REQUEST); - this.buffalo.writeUInt16(concentratorType); - this.buffalo.writeUInt8(radius); + async ezspSendManyToOneRouteRequest(concentratorType: number, radius: number): Promise { + this.startCommand(EzspFrameID.SEND_MANY_TO_ONE_ROUTE_REQUEST); + this.buffalo.writeUInt16(concentratorType); + this.buffalo.writeUInt8(radius); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readStatus(this.version); + + return status; + } + + /** + * Periodically request any pending data from our parent. Setting interval to 0 + * or units to EMBER_EVENT_INACTIVE will generate a single poll. + * @param interval uint16_t The time between polls. Note that the timer clock is free running and is not synchronized with this command. + * This means that the time will be between interval and (interval - 1). The maximum interval is 32767. + * @param units The units for interval. + * @param failureLimit uint8_t The number of poll failures that will be tolerated before a pollCompleteHandler callback is generated. + * A value of zero will result in a callback for every poll. Any status value apart from EMBER_SUCCESS + * and EMBER_MAC_NO_DATA is counted as a failure. + * @returns The result of sending the first poll. + */ + async ezspPollForData(interval: number, units: EmberEventUnits, failureLimit: number): Promise { + this.startCommand(EzspFrameID.POLL_FOR_DATA); + this.buffalo.writeUInt16(interval); + this.buffalo.writeUInt8(units); + this.buffalo.writeUInt8(failureLimit); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readStatus(this.version); + + return status; + } + + /** + * Callback + * Indicates the result of a data poll to the parent of the local node. + * @param status An EmberStatus value: + * - EMBER_SUCCESS - Data was received in response to the poll. + * - EMBER_MAC_NO_DATA - No data was pending. + * - EMBER_DELIVERY_FAILED - The poll message could not be sent. + * - EMBER_MAC_NO_ACK_RECEIVED - The poll message was sent but not acknowledged by the parent. + */ + ezspPollCompleteHandler(status: SLStatus): void { + logger.debug(`ezspPollCompleteHandler(): callback called with: [status=${SLStatus[status]}]`, NS); + } + + /** + * Set a flag to indicate that a message is pending for a child. + * The next time that the child polls, it will be informed that it has a pending message. + * The message is sent from emberPollHandler, which is called when the child requests data. + * @param childId The ID of the child that just polled for data. + * @returns + * - SL_STATUS_OK - The next time that the child polls, it will be informed that it has pending data. + * - SL_STATUS_NOT_JOINED - The child identified by childId is not our child. + */ + async ezspSetMessageFlag(childId: NodeId): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_MESSAGE_FLAG); + this.buffalo.writeUInt16(childId); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Clear a flag to indicate that there are no more messages for a child. + * The next time the child polls, it will be informed that it does not have any pending messages. + * @param childId The ID of the child that no longer has pending messages. + * @returns + * - SL_STATUS_OK - The next time that the child polls, it will be informed that it does not have any pending messages. + * - SL_STATUS_NOT_JOINED - The child identified by childId is not our child. + */ + async ezspClearMessageFlag(childId: NodeId): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.CLEAR_MESSAGE_FLAG); + this.buffalo.writeUInt16(childId); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Callback + * Indicates that the local node received a data poll from a child. + * @param childId The node ID of the child that is requesting data. + * @param transmitExpected True if transmit is expected, false otherwise. + */ + ezspPollHandler(childId: NodeId, transmitExpected: boolean): void { + logger.debug(`ezspPollHandler(): callback called with: [childId=${childId}], [transmitExpected=${transmitExpected}]`, NS); + } + + /** + * Add a child to the child/neighbor table only on SoC, allowing direct manipulation of these tables by the application. + * This can affect the network functionality, and needs to be used wisely. + * If used appropriately, the application can maintain more than the maximum of children provided by the stack. + * @param shortId The preferred short ID of the node. + * @param longId The long ID of the node. + * @param nodeType The nodetype e.g., SL_ZIGBEE_ROUTER defining, if this would be added to the child table or neighbor table. + * @returns + * - SL_STATUS_OK - This node has been successfully added. + * - SL_STATUS_FAIL - The child was not added to the child/neighbor table. + */ + async ezspAddChild(shortId: NodeId, longId: EUI64, nodeType: EmberNodeType): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } - const sendStatus: EzspStatus = await this.sendCommand(); + this.startCommand(EzspFrameID.ADD_CHILD); + this.buffalo.writeUInt16(shortId); + this.buffalo.writeIeeeAddr(longId); + this.buffalo.writeUInt8(nodeType); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readUInt32(); + return status; } /** - * Periodically request any pending data from our parent. Setting interval to 0 - * or units to EMBER_EVENT_INACTIVE will generate a single poll. - * @param interval uint16_t The time between polls. Note that the timer clock is free running and is not synchronized with this command. - * This means that the time will be between interval and (interval - 1). The maximum interval is 32767. - * @param units The units for interval. - * @param failureLimit uint8_t The number of poll failures that will be tolerated before a pollCompleteHandler callback is generated. - * A value of zero will result in a callback for every poll. Any status value apart from EMBER_SUCCESS - * and EMBER_MAC_NO_DATA is counted as a failure. - * @returns The result of sending the first poll. + * Remove a node from child/neighbor table only on SoC, allowing direct manipulation of these tables by the application. + * This can affect the network functionality, and needs to be used wisely. + * @param childEui64 The long ID of the node. + * @returns + * - SL_STATUS_OK - This node has been successfully removed. + * - SL_STATUS_FAIL - The node was not found in either of the child or neighbor tables. */ - async ezspPollForData(interval: number, units: EmberEventUnits, failureLimit: number): Promise { - this.startCommand(EzspFrameID.POLL_FOR_DATA); - this.buffalo.writeUInt16(interval); - this.buffalo.writeUInt8(units); - this.buffalo.writeUInt8(failureLimit); + async ezspRemoveChild(childEui64: EUI64): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.REMOVE_CHILD); + this.buffalo.writeIeeeAddr(childEui64); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readUInt32(); + return status; } /** - * Callback - * Indicates the result of a data poll to the parent of the local node. - * @param status An EmberStatus value: - * - EMBER_SUCCESS - Data was received in response to the poll. - * - EMBER_MAC_NO_DATA - No data was pending. - * - EMBER_DELIVERY_FAILED - The poll message could not be sent. - * - EMBER_MAC_NO_ACK_RECEIVED - The poll message was sent but not acknowledged by the parent. + * Remove a neighbor from neighbor table only on SoC, allowing direct manipulation of neighbor table by the application. + * This can affect the network functionality, and needs to be used wisely. + * @param shortId The short ID of the neighbor. + * @param longId The long ID of the neighbor. */ - ezspPollCompleteHandler(status: EmberStatus): void { - logger.debug(`ezspPollCompleteHandler(): callback called with: [status=${EmberStatus[status]}]`, NS); - } + async ezspRemoveNeighbor(shortId: NodeId, longId: EUI64): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } - /** - * Callback - * Indicates that the local node received a data poll from a child. - * @param childId The node ID of the child that is requesting data. - * @param transmitExpected True if transmit is expected, false otherwise. - */ - ezspPollHandler(childId: EmberNodeId, transmitExpected: boolean): void { - logger.debug(`ezspPollHandler(): callback called with: [childId=${childId}], [transmitExpected=${transmitExpected}]`, NS); - } + this.startCommand(EzspFrameID.REMOVE_NEIGHBOR); + this.buffalo.writeUInt16(shortId); + this.buffalo.writeIeeeAddr(longId); + + const sendStatus = await this.sendCommand(); - /** - * Callback - * A callback indicating a message has been received containing the EUI64 of the - * sender. This callback is called immediately before the incomingMessageHandler - * callback. It is not called if the incoming message did not contain the EUI64 - * of the sender. - * @param senderEui64 The EUI64 of the sender - */ - ezspIncomingSenderEui64Handler(senderEui64: EmberEUI64): void { - logger.debug(`ezspIncomingSenderEui64Handler(): callback called with: [senderEui64=${senderEui64}]`, NS); + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + // XXX: watchout for update here, Silabs likely forgot status return } /** @@ -4077,20 +5200,14 @@ export class Ezsp extends EventEmitter { * @param type The type of the incoming message. One of the following: EMBER_INCOMING_UNICAST, EMBER_INCOMING_UNICAST_REPLY, * EMBER_INCOMING_MULTICAST, EMBER_INCOMING_MULTICAST_LOOPBACK, EMBER_INCOMING_BROADCAST, EMBER_INCOMING_BROADCAST_LOOPBACK * @param apsFrame EmberApsFrame * The APS frame from the incoming message. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception. - * @param sender The sender of the message. - * @param bindingIndex uint8_t The index of a binding that matches the message or 0xFF if there is no matching binding. - * @param addressIndex uint8_t The index of the entry in the address table that matches the sender of the message - * or 0xFF if there is no matching entry. + * @param packetInfo Miscellanous message information. * @param messageContents uint8_t * The incoming message. */ - ezspIncomingMessageHandler(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, lastHopRssi: number, - sender: EmberNodeId, bindingIndex: number, addressIndex: number, messageContents: Buffer): void { + ezspIncomingMessageHandler(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, packetInfo: EmberRxPacketInfo, messageContents: Buffer) + : void { logger.debug( `ezspIncomingMessageHandler(): callback called with: [type=${EmberIncomingMessageType[type]}], [apsFrame=${JSON.stringify(apsFrame)}], ` - + `[lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}], [sender=${sender}], [bindingIndex=${bindingIndex}], ` - + `[addressIndex=${addressIndex}], [messageContents=${messageContents.toString('hex')}]`, + + `[packetInfo:${JSON.stringify(packetInfo)}], [messageContents=${messageContents.toString('hex')}]`, NS, ); @@ -4103,7 +5220,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { // 64-bit address for the remote device const eui64 = zdoBuffalo.readIeeeAddr(); @@ -4136,7 +5253,7 @@ export class Ezsp extends EventEmitter { const payload: IEEEAddressResponsePayload = {eui64, nodeId, assocDevList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4145,7 +5262,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO NETWORK_ADDRESS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { // 64-bit address for the remote device const eui64 = zdoBuffalo.readIeeeAddr(); @@ -4177,7 +5294,7 @@ export class Ezsp extends EventEmitter { const payload: NetworkAddressResponsePayload = {eui64, nodeId, assocDevList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4186,7 +5303,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const nodeId = zdoBuffalo.readUInt16(); const endpointCount = zdoBuffalo.readUInt8(); @@ -4200,7 +5317,7 @@ export class Ezsp extends EventEmitter { const payload: MatchDescriptorsResponsePayload = {nodeId, endpointList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4209,7 +5326,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO SIMPLE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const nodeId = zdoBuffalo.readUInt16(); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -4237,7 +5354,7 @@ export class Ezsp extends EventEmitter { outClusterList, }; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4246,7 +5363,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO NODE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const nodeId = zdoBuffalo.readUInt16(); // in bits: [logical type: 3] [complex description available: 1] [user descriptor available: 1] [reserved/unused: 3] @@ -4312,7 +5429,7 @@ export class Ezsp extends EventEmitter { stackRevision }; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4321,7 +5438,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const nodeId = zdoBuffalo.readUInt16(); // mode: 0000 == receiver sync'ed with receiver on when idle subfield of the node descriptor @@ -4357,7 +5474,7 @@ export class Ezsp extends EventEmitter { currentPowerSourceLevel }; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4366,7 +5483,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO ACTIVE_ENDPOINTS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const nodeId = zdoBuffalo.readUInt16(); const endpointCount = zdoBuffalo.readUInt8(); @@ -4379,7 +5496,7 @@ export class Ezsp extends EventEmitter { const payload: ActiveEndpointsResponsePayload = {nodeId, endpointList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4388,7 +5505,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO LQI_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { // 0x00-0xFF, total number of neighbor table entries within the remote device const neighborTableEntries = zdoBuffalo.readUInt8(); @@ -4401,7 +5518,7 @@ export class Ezsp extends EventEmitter { const entryList: ZDOLQITableEntry[] = []; for (let i = 0; i < entryCount; i++) { - const extendedPanId = zdoBuffalo.readListUInt8(EXTENDED_PAN_ID_SIZE); + const extendedPanId = zdoBuffalo.readListUInt8(ZSpec.EXTENDED_PAN_ID_SIZE); const eui64 = zdoBuffalo.readIeeeAddr(); const nodeId = zdoBuffalo.readUInt16(); const deviceTypeByte = zdoBuffalo.readUInt8(); @@ -4429,7 +5546,7 @@ export class Ezsp extends EventEmitter { const payload: LQITableResponsePayload = {neighborTableEntries, entryList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4438,7 +5555,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO ROUTING_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { // 0x00-0xFF, total number of routing table entries within the remote device const routingTableEntries = zdoBuffalo.readUInt8(); @@ -4471,7 +5588,7 @@ export class Ezsp extends EventEmitter { const payload: RoutingTableResponsePayload = {routingTableEntries, entryList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4480,7 +5597,7 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { logger.debug(`<=== [ZDO BINDING_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const bindingTableEntries = zdoBuffalo.readUInt8(); const startIndex = zdoBuffalo.readUInt8(); @@ -4511,7 +5628,7 @@ export class Ezsp extends EventEmitter { const payload: BindingTableResponsePayload = {bindingTableEntries, entryList}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } break; } @@ -4519,28 +5636,28 @@ export class Ezsp extends EventEmitter { const status: EmberZdoStatus = zdoBuffalo.readUInt8(); logger.debug(`<=== [ZDO BIND_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); break; } case UNBIND_RESPONSE:{ const status: EmberZdoStatus = zdoBuffalo.readUInt8(); logger.debug(`<=== [ZDO UNBIND_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); break; } case LEAVE_RESPONSE: { const status: EmberZdoStatus = zdoBuffalo.readUInt8(); logger.debug(`<=== [ZDO LEAVE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); break; } case PERMIT_JOINING_RESPONSE: { const status: EmberZdoStatus = zdoBuffalo.readUInt8(); logger.debug(`<=== [ZDO PERMIT_JOINING_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); break; } case END_DEVICE_ANNOUNCE: { @@ -4554,7 +5671,7 @@ export class Ezsp extends EventEmitter { const payload: EndDeviceAnnouncePayload = {nodeId, eui64, capabilities: getMacCapFlags(capabilities)}; // this one gets its own event since its purpose is to pass an event up to Z2M - this.emit(EzspEvents.END_DEVICE_ANNOUNCE, sender, apsFrame, payload); + this.emit(EzspEvents.END_DEVICE_ANNOUNCE, packetInfo.senderShortId, apsFrame, payload); break; } case PARENT_ANNOUNCE_RESPONSE: { @@ -4563,10 +5680,10 @@ export class Ezsp extends EventEmitter { if (status !== EmberZdoStatus.ZDP_SUCCESS) { // status should always be NOT_SUPPORTED here logger.debug(`<=== [ZDO PARENT_ANNOUNCE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); } else { const numberOfChildren = zdoBuffalo.readUInt8(); - const children: EmberEUI64[] = []; + const children: EUI64[] = []; for (let i = 0; i < numberOfChildren; i++) { const childEui64 = zdoBuffalo.readIeeeAddr(); @@ -4580,21 +5697,21 @@ export class Ezsp extends EventEmitter { const payload: ParentAnnounceResponsePayload = {children}; - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); + this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); } } default: { - logger.info(`<=== [ZDO clusterId=${apsFrame.clusterId} sender=${sender}] Support not implemented upstream.`, NS); + logger.info(`<=== [ZDO clusterId=${apsFrame.clusterId} sender=${packetInfo.senderShortId}] Support not implemented upstream.`, NS); break; } } } else if (apsFrame.profileId === ZSpec.HA_PROFILE_ID || apsFrame.profileId === ZSpec.WILDCARD_PROFILE_ID) { - this.emit(EzspEvents.INCOMING_MESSAGE, type, apsFrame, lastHopLqi, sender, messageContents); + this.emit(EzspEvents.INCOMING_MESSAGE, type, apsFrame, packetInfo.lastHopLqi, packetInfo.senderShortId, messageContents); } else if (apsFrame.profileId === ZSpec.GP_PROFILE_ID) { // only broadcast loopback in here } } - + /** * Sets source route discovery(MTORR) mode to on, off, reschedule * @param mode uint8_t Source route discovery mode: off:0, on:1, reschedule:2 @@ -4604,10 +5721,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_SOURCE_ROUTE_DISCOVERY_MODE); this.buffalo.writeUInt8(mode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const remainingTime = this.buffalo.readUInt32(); @@ -4618,12 +5735,12 @@ export class Ezsp extends EventEmitter { /** * Callback * A callback indicating that a many-to-one route to the concentrator with the given short and long id is available for use. - * @param EmberNodeId The short id of the concentrator. + * @param NodeId The short id of the concentrator. * @param longId The EUI64 of the concentrator. * @param cost uint8_t The path cost to the concentrator. The cost may decrease as additional route request packets * for this discovery arrive, but the callback is made only once. */ - ezspIncomingManyToOneRouteRequestHandler(source: EmberNodeId, longId: EmberEUI64, cost: number): void { + ezspIncomingManyToOneRouteRequestHandler(source: NodeId, longId: EUI64, cost: number): void { logger.debug(`ezspIncomingManyToOneRouteRequestHandler(): callback called with: [source=${source}], [longId=${longId}], [cost=${cost}]`, NS); } @@ -4652,8 +5769,8 @@ export class Ezsp extends EventEmitter { * @param status ::EMBER_SOURCE_ROUTE_FAILURE, ::EMBER_MANY_TO_ONE_ROUTE_FAILURE, ::EMBER_MAC_INDIRECT_TIMEOUT * @param target The short id of the remote node. */ - ezspIncomingRouteErrorHandler(status: EmberStatus, target: EmberNodeId): void { - logger.debug(`ezspIncomingRouteErrorHandler(): callback called with: [status=${EmberStatus[status]}], [target=${target}]`, NS); + ezspIncomingRouteErrorHandler(status: SLStatus, target: NodeId): void { + logger.debug(`ezspIncomingRouteErrorHandler(): callback called with: [status=${SLStatus[status]}], [target=${target}]`, NS); // NOTE: This can trigger immediately after removal of a device with status MAC_INDIRECT_TIMEOUT } @@ -4670,7 +5787,7 @@ export class Ezsp extends EventEmitter { * @param errorCode uint8_t One byte over-the-air error code from network status message * @param target The short ID of the remote node */ - ezspIncomingNetworkStatusHandler(errorCode: EmberStackError, target: EmberNodeId): void { + ezspIncomingNetworkStatusHandler(errorCode: EmberStackError, target: NodeId): void { logger.debug(`ezspIncomingNetworkStatusHandler(): callback called with: [errorCode=${EmberStackError[errorCode]}], [target=${target}]`, NS); logger.info(`Received network/route error ${EmberStackError[errorCode]} for "${target}".`, NS); } @@ -4678,15 +5795,15 @@ export class Ezsp extends EventEmitter { /** * Callback * Reports the arrival of a route record command frame. - * @param EmberNodeId The source of the route record. - * @param EmberEUI64 The EUI64 of the source. + * @param NodeId The source of the route record. + * @param EUI64 The EUI64 of the source. * @param lastHopLqi uint8_t The link quality from the node that last relayed the route record. * @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception. * @param uint8_t The number of relays in relayList. * @param relayList uint8_t * The route record. Each relay in the list is an uint16_t node ID. * The list is passed as uint8_t * to avoid alignment problems. */ - ezspIncomingRouteRecordHandler(source: EmberNodeId, sourceEui: EmberEUI64, lastHopLqi: number, + ezspIncomingRouteRecordHandler(source: NodeId, sourceEui: EUI64, lastHopLqi: number, lastHopRssi: number, relayCount: number, relayList: number[]): void { logger.debug(`ezspIncomingRouteRecordHandler(): callback called with: [source=${source}], [sourceEui=${sourceEui}], ` + `[lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}], [relayCount=${relayCount}], [relayList=${relayList}]`, NS); @@ -4700,19 +5817,20 @@ export class Ezsp extends EventEmitter { * @returns EMBER_SUCCESS if the source route was successfully stored, and * EMBER_NO_BUFFERS otherwise. */ - async ezspSetSourceRoute(destination: EmberNodeId, relayList: number[]): Promise { + async ezspSetSourceRoute(destination: NodeId, relayList: number[]): Promise { this.startCommand(EzspFrameID.SET_SOURCE_ROUTE); this.buffalo.writeUInt16(destination); this.buffalo.writeUInt8(relayList.length); this.buffalo.writeListUInt16(relayList); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -4723,19 +5841,20 @@ export class Ezsp extends EventEmitter { * @param parentShortId The parent node of the destination node. * @returns EMBER_SUCCESS if send was successful */ - async ezspUnicastCurrentNetworkKey(targetShort: EmberNodeId, targetLong: EmberEUI64, parentShortId: EmberNodeId): Promise { + async ezspUnicastCurrentNetworkKey(targetShort: NodeId, targetLong: EUI64, parentShortId: NodeId): Promise { this.startCommand(EzspFrameID.UNICAST_CURRENT_NETWORK_KEY); this.buffalo.writeUInt16(targetShort); this.buffalo.writeIeeeAddr(targetLong); this.buffalo.writeUInt16(parentShortId); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -4751,112 +5870,85 @@ export class Ezsp extends EventEmitter { async ezspAddressTableEntryIsActive(addressTableIndex: number): Promise { this.startCommand(EzspFrameID.ADDRESS_TABLE_ENTRY_IS_ACTIVE); this.buffalo.writeUInt8(addressTableIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const active = this.buffalo.readUInt8() === 1 ? true : false; + + const active = this.buffalo.readUInt8() !== 0; return active; } /** - * Sets the EUI64 of an address table entry. This function will also check other - * address table entries, the child table and the neighbor table to see if the - * node ID for the given EUI64 is already known. If known then this function - * will also set node ID. If not known it will set the node ID to - * EMBER_UNKNOWN_NODE_ID. - * @param addressTableIndex uint8_t The index of an address table entry. - * @param eui64 The EUI64 to use for the address table entry. - * @returns - * - EMBER_SUCCESS if the EUI64 was successfully set, - * - EMBER_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise. + * Sets the EUI64 and short ID of an address table entry. + * Usually the application will not need to set the short ID in the address table. + * Once the remote EUI64 is set the stack is capable of figuring out the short ID on its own. + * However, in cases where the application does set the short ID, the application must set the remote EUI64 prior to setting the short ID. + * This function will also check other address table entries, the child table and the neighbor table to see + * if the node ID for the given EUI64 is already known. + * If known then this function will set node ID. If not known it will set the node ID to SL_ZIGBEE_UNKNOWN_NODE_ID. + * @param addressTableIndex + * @param eui64 + * @param id + * @returns SL_STATUS_OK if the information was successfully set, and SL_STATUS_ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise. */ - async ezspSetAddressTableRemoteEui64(addressTableIndex: number, eui64: EmberEUI64): Promise { - this.startCommand(EzspFrameID.SET_ADDRESS_TABLE_REMOTE_EUI64); - this.buffalo.writeUInt8(addressTableIndex); - this.buffalo.writeIeeeAddr(eui64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + async ezspSetAddressTableInfo(addressTableIndex: number, eui64: EUI64, id: NodeId): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - /** - * Sets the short ID of an address table entry. Usually the application will not - * need to set the short ID in the address table. Once the remote EUI64 is set - * the stack is capable of figuring out the short ID on its own. However, in - * cases where the application does set the short ID, the application must set - * the remote EUI64 prior to setting the short ID. - * @param addressTableIndex uint8_t The index of an address table entry. - * @param id The short ID corresponding to the remote node whose EUI64 is stored in the address table at the given index - * or EMBER_TABLE_ENTRY_UNUSED_NODE_ID which indicates that the entry stored in the address table at the given index is not in use. - */ - async ezspSetAddressTableRemoteNodeId(addressTableIndex: number, id: EmberNodeId): Promise { - this.startCommand(EzspFrameID.SET_ADDRESS_TABLE_REMOTE_NODE_ID); + this.startCommand(EzspFrameID.SET_ADDRESS_TABLE_INFO); this.buffalo.writeUInt8(addressTableIndex); + this.buffalo.writeIeeeAddr(eui64); this.buffalo.writeUInt16(id); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readUInt32(); + + return status; } /** - * Gets the EUI64 of an address table entry. - * @param addressTableIndex uint8_t The index of an address table entry. + * Gets the EUI64 and short ID of an address table entry. + * @param addressTableIndex + * @returns An sl_status_t value indicating success or the reason for failure. + * @returns One of the following: + * - The short ID corresponding to the remote node whose EUI64 is stored in the address table at the given index. + * - SL_ZIGBEE_UNKNOWN_NODE_ID: + * Indicates that the EUI64 stored in the address table at the given index is valid but the short ID is currently unknown. + * - SL_ZIGBEE_DISCOVERY_ACTIVE_NODE_ID: + * Indicates that the EUI64 stored in the address table at the given location is valid and network address discovery is underway. + * - SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID: + * Indicates that the entry stored in the address table at the given index is not in use. * @returns The EUI64 of the address table entry is copied to this location. */ - async ezspGetAddressTableRemoteEui64(addressTableIndex: number): Promise { - this.startCommand(EzspFrameID.GET_ADDRESS_TABLE_REMOTE_EUI64); + async ezspGetAddressTableInfo(addressTableIndex: number): Promise<[SLStatus, nodeId: NodeId, eui64: EUI64]> { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_ADDRESS_TABLE_INFO); this.buffalo.writeUInt8(addressTableIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + const status = this.buffalo.readUInt32(); + const nodeId = this.buffalo.readUInt16(); const eui64 = this.buffalo.readIeeeAddr(); - return eui64; - } - - /** - * Gets the short ID of an address table entry. - * @param addressTableIndex uint8_t The index of an address table entry. - * @returns One of the following: The short ID corresponding to the remote node - * whose EUI64 is stored in the address table at the given index. - * - EMBER_UNKNOWN_NODE_ID - Indicates that the EUI64 stored in the address table - * at the given index is valid but the short ID is currently unknown. - * - EMBER_DISCOVERY_ACTIVE_NODE_ID - Indicates that the EUI64 stored in the - * address table at the given location is valid and network address discovery is - * underway. - * - EMBER_TABLE_ENTRY_UNUSED_NODE_ID - Indicates that the entry stored - * in the address table at the given index is not in use. - */ - async ezspGetAddressTableRemoteNodeId(addressTableIndex: number): Promise { - this.startCommand(EzspFrameID.GET_ADDRESS_TABLE_REMOTE_NODE_ID); - this.buffalo.writeUInt8(addressTableIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const nodeId: EmberNodeId = this.buffalo.readUInt16(); - return nodeId; + return [status, nodeId, eui64]; } /** @@ -4873,15 +5965,23 @@ export class Ezsp extends EventEmitter { * @param extendedTimeout true if the retry interval should be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. * false if the normal retry interval should be used. */ - async ezspSetExtendedTimeout(remoteEui64: EmberEUI64, extendedTimeout: boolean): Promise { + async ezspSetExtendedTimeout(remoteEui64: EUI64, extendedTimeout: boolean): Promise { this.startCommand(EzspFrameID.SET_EXTENDED_TIMEOUT); this.buffalo.writeIeeeAddr(remoteEui64); this.buffalo.writeUInt8(extendedTimeout ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); + } + + if (this.version < 0x0E) { + return SLStatus.OK; + } else { + const status = this.buffalo.readUInt32(); + + return status; } } @@ -4890,22 +5990,29 @@ export class Ezsp extends EventEmitter { * retransmissions of a retried unicast message by * EMBER_INDIRECT_TRANSMISSION_TIMEOUT. * @param remoteEui64 The address of the node for which the timeout is to be returned. - * @returns true if the retry interval will be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT - * and false if the normal retry interval will be used. + * @returns + * - SL_STATUS_OK if the retry interval will be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT + * - SL_STATUS_FAIL if the normal retry interval will be used. */ - async ezspGetExtendedTimeout(remoteEui64: EmberEUI64): Promise { + async ezspGetExtendedTimeout(remoteEui64: EUI64): Promise { this.startCommand(EzspFrameID.GET_EXTENDED_TIMEOUT); this.buffalo.writeIeeeAddr(remoteEui64); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const extendedTimeout = this.buffalo.readUInt8() === 1 ? true : false; - return extendedTimeout; + if (this.version < 0x0E) { + const extendedTimeout = this.buffalo.readUInt8() !== 0; + + return extendedTimeout ? SLStatus.OK : SLStatus.FAIL; + } else { + const status = this.buffalo.readUInt32(); + + return status; + } } /** @@ -4923,30 +6030,30 @@ export class Ezsp extends EventEmitter { * were successfully modified, and EMBER_ADDRESS_TABLE_ENTRY_IS_ACTIVE * otherwise. * @returns oldEui64 The EUI64 of the address table entry before it was modified. - * @returns oldId EmberNodeId * One of the following: The short ID corresponding to the EUI64 before it was modified. + * @returns oldId NodeId * One of the following: The short ID corresponding to the EUI64 before it was modified. * EMBER_UNKNOWN_NODE_ID if the short ID was unknown. EMBER_DISCOVERY_ACTIVE_NODE_ID if discovery of the short ID was underway. * EMBER_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry was unused. * @returns oldExtendedTimeouttrue bool * if the retry interval was being increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. * false if the normal retry interval was being used. */ - async ezspReplaceAddressTableEntry(addressTableIndex: number, newEui64: EmberEUI64, newId: EmberNodeId, newExtendedTimeout: boolean): - Promise<[EmberStatus, oldEui64: EmberEUI64, oldId: EmberNodeId, oldExtendedTimeout: boolean]> { + async ezspReplaceAddressTableEntry(addressTableIndex: number, newEui64: EUI64, newId: NodeId, newExtendedTimeout: boolean): + Promise<[SLStatus, oldEui64: EUI64, oldId: NodeId, oldExtendedTimeout: boolean]> { this.startCommand(EzspFrameID.REPLACE_ADDRESS_TABLE_ENTRY); this.buffalo.writeUInt8(addressTableIndex); this.buffalo.writeIeeeAddr(newEui64); this.buffalo.writeUInt16(newId); this.buffalo.writeUInt8(newExtendedTimeout ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const oldEui64 = this.buffalo.readIeeeAddr(); const oldId = this.buffalo.readUInt16(); - const oldExtendedTimeout = this.buffalo.readUInt8() === 1 ? true : false; + const oldExtendedTimeout = this.buffalo.readUInt8() !== 0; return [status, oldEui64, oldId, oldExtendedTimeout]; } @@ -4958,17 +6065,17 @@ export class Ezsp extends EventEmitter { * @returns The short ID of the node or EMBER_NULL_NODE_ID if the short ID is not * known. */ - async ezspLookupNodeIdByEui64(eui64: EmberEUI64): Promise { + async ezspLookupNodeIdByEui64(eui64: EUI64): Promise { this.startCommand(EzspFrameID.LOOKUP_NODE_ID_BY_EUI64); this.buffalo.writeIeeeAddr(eui64); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const nodeId: EmberNodeId = this.buffalo.readUInt16(); + const nodeId: NodeId = this.buffalo.readUInt16(); return nodeId; } @@ -4981,17 +6088,17 @@ export class Ezsp extends EventEmitter { * not known. * @returns eui64 The EUI64 of the node. */ - async ezspLookupEui64ByNodeId(nodeId: EmberNodeId): Promise<[EmberStatus, eui64: EmberEUI64]> { + async ezspLookupEui64ByNodeId(nodeId: NodeId): Promise<[SLStatus, eui64: EUI64]> { this.startCommand(EzspFrameID.LOOKUP_EUI64_BY_NODE_ID); this.buffalo.writeUInt16(nodeId); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const eui64 = this.buffalo.readIeeeAddr(); return [status, eui64]; @@ -5003,17 +6110,17 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberMulticastTableEntry * The contents of the multicast entry. */ - async ezspGetMulticastTableEntry(index: number): Promise<[EmberStatus, value: EmberMulticastTableEntry]> { + async ezspGetMulticastTableEntry(index: number): Promise<[SLStatus, value: EmberMulticastTableEntry]> { this.startCommand(EzspFrameID.GET_MULTICAST_TABLE_ENTRY); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const value = this.buffalo.readEmberMulticastTableEntry(); return [status, value]; @@ -5025,18 +6132,19 @@ export class Ezsp extends EventEmitter { * @param EmberMulticastTableEntry * The contents of the multicast entry. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetMulticastTableEntry(index: number, value: EmberMulticastTableEntry): Promise { + async ezspSetMulticastTableEntry(index: number, value: EmberMulticastTableEntry): Promise { this.startCommand(EzspFrameID.SET_MULTICAST_TABLE_ENTRY); this.buffalo.writeUInt8(index); this.buffalo.writeEmberMulticastTableEntry(value); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5049,7 +6157,7 @@ export class Ezsp extends EventEmitter { * application should discontinue any other use of the id. * @param id The short id for which a conflict was detected */ - ezspIdConflictHandler(id: EmberNodeId): void { + ezspIdConflictHandler(id: NodeId): void { logger.debug(`ezspIdConflictHandler(): callback called with: [id=${id}]`, NS); logger.warning(`An ID conflict was detected for network address "${id}". Corresponding devices kicked from the network.`, NS); @@ -5064,37 +6172,18 @@ export class Ezsp extends EventEmitter { * @param erase Erase the node type or not * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspWriteNodeData(erase: boolean): Promise { + async ezspWriteNodeData(erase: boolean): Promise { this.startCommand(EzspFrameID.WRITE_NODE_DATA); this.buffalo.writeUInt8(erase ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Transmits the given message without modification. The MAC header is assumed - * to be configured in the message at the time this function is called. - * @param messageContents uint8_t * The raw message. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspSendRawMessage(messageContents: Buffer): Promise { - this.startCommand(EzspFrameID.SEND_RAW_MESSAGE); - this.buffalo.writePayload(messageContents); - - const sendStatus: EzspStatus = await this.sendCommand(); + const status = this.buffalo.readStatus(this.version); - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -5104,21 +6193,22 @@ export class Ezsp extends EventEmitter { * @param messageContents uint8_t * The raw message. * @param priority uint8_t transmit priority. * @param useCca Should we enable CCA or not. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An sl_status_t value indicating success or the reason for failure. */ - async ezspSendRawMessageExtended(messageContents: Buffer, priority: number, useCca: boolean): Promise { - this.startCommand(EzspFrameID.SEND_RAW_MESSAGE_EXTENDED); + async ezspSendRawMessage(messageContents: Buffer, priority: EmberTransmitPriority, useCca: boolean): Promise { + this.startCommand(EzspFrameID.SEND_RAW_MESSAGE); this.buffalo.writePayload(messageContents); this.buffalo.writeUInt8(priority); this.buffalo.writeUInt8(useCca ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5127,14 +6217,12 @@ export class Ezsp extends EventEmitter { * A callback invoked by the EmberZNet stack when a MAC passthrough message is * received. * @param messageType The type of MAC passthrough message received. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during reception. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. + * @param packetInfo Information about the incoming packet. * @param messageContents uint8_t * The raw message that was received. */ - ezspMacPassthroughMessageHandler(messageType: EmberMacPassthroughType, lastHopLqi: number, lastHopRssi: number, messageContents: Buffer): void { - logger.debug(`ezspMacPassthroughMessageHandler(): callback called with: [messageType=${messageType}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}], [messageContents=${messageContents.toString('hex')}]`, NS); + ezspMacPassthroughMessageHandler(messageType: EmberMacPassthroughType, packetInfo: EmberRxPacketInfo, messageContents: Buffer): void { + logger.debug(`ezspMacPassthroughMessageHandler(): callback called with: [messageType=${messageType}], ` + + `[packetInfo=${JSON.stringify(packetInfo)}], [messageContents=${messageContents.toString('hex')}]`, NS); } /** @@ -5143,15 +6231,13 @@ export class Ezsp extends EventEmitter { * matched one of the application's configured MAC filters. * @param filterIndexMatch uint8_t The index of the filter that was matched. * @param legacyPassthroughType The type of MAC passthrough message received. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during reception. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. + * @param packetInfo Information about the incoming packet. * @param messageContents uint8_t * The raw message that was received. */ - ezspMacFilterMatchMessageHandler(filterIndexMatch: number, legacyPassthroughType: EmberMacPassthroughType, lastHopLqi: number, - lastHopRssi: number, messageContents: Buffer): void { + ezspMacFilterMatchMessageHandler(filterIndexMatch: number, legacyPassthroughType: EmberMacPassthroughType, packetInfo: EmberRxPacketInfo, + messageContents: Buffer): void { logger.debug(`ezspMacFilterMatchMessageHandler(): callback called with: [filterIndexMatch=${filterIndexMatch}], ` - + `[legacyPassthroughType=${legacyPassthroughType}], [lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}], ` + + `[legacyPassthroughType=${legacyPassthroughType}], [packetInfo=${JSON.stringify(packetInfo)}], ` + `[messageContents=${messageContents.toString('hex')}]`, NS); // TODO: needs triple-checking, this is only valid for InterPAN messages @@ -5161,8 +6247,8 @@ export class Ezsp extends EventEmitter { // eslint-disable-next-line @typescript-eslint/no-unused-vars const sequence = msgBuffalo.readUInt8(); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const destPanId: EmberPanId = msgBuffalo.readUInt16(); - let destAddress: EmberEUI64 | EmberNodeId; + const destPanId: PanId = msgBuffalo.readUInt16(); + let destAddress: EUI64 | NodeId; if (macFrameControl === LONG_DEST_FRAME_CONTROL) { destAddress = msgBuffalo.readIeeeAddr(); @@ -5174,8 +6260,8 @@ export class Ezsp extends EventEmitter { return; } - const sourcePanId: EmberPanId = msgBuffalo.readUInt16(); - const sourceAddress: EmberEUI64 = msgBuffalo.readIeeeAddr(); + const sourcePanId: PanId = msgBuffalo.readUInt16(); + const sourceAddress: EUI64 = msgBuffalo.readIeeeAddr(); // Now that we know the correct MAC length, verify the interpan frame is the correct length. let remainingLength = msgBuffalo.getBufferLength() - msgBuffalo.getPosition(); @@ -5233,7 +6319,7 @@ export class Ezsp extends EventEmitter { const payload = msgBuffalo.readRest(); if (profileId === ZSpec.TOUCHLINK_PROFILE_ID && clusterId === Clusters.touchlink.ID) { - this.emit(EzspEvents.TOUCHLINK_MESSAGE, sourcePanId, sourceAddress, groupId, lastHopLqi, payload); + this.emit(EzspEvents.TOUCHLINK_MESSAGE, sourcePanId, sourceAddress, groupId, packetInfo.lastHopLqi, payload); } } @@ -5241,10 +6327,12 @@ export class Ezsp extends EventEmitter { * Callback * A callback invoked by the EmberZNet stack when the MAC has finished * transmitting a raw message. + * @param messageContents (v14+) * @param status EMBER_SUCCESS if the transmission was successful, or EMBER_DELIVERY_FAILED if not */ - ezspRawTransmitCompleteHandler(status: EmberStatus): void { - logger.debug(`ezspRawTransmitCompleteHandler(): callback called with: [status=${EmberStatus[status]}]`, NS); + ezspRawTransmitCompleteHandler(messageContents: Buffer, status: SLStatus): void { + logger.debug(`ezspRawTransmitCompleteHandler(): callback called with: [messageContents=${messageContents.toString('hex')}], ` + + `[status=${SLStatus[status]}]`, NS); } /** @@ -5259,11 +6347,33 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_MAC_POLL_FAILURE_WAIT_TIME); this.buffalo.writeUInt32(waitBeforeRetryIntervalMs); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + } + + /** + * Returns the maximum number of no-ack retries that will be attempted + * @returns Max MAC retries + */ + async ezspGetMaxMacRetries(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.GET_MAX_MAC_RETRIES); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const retries = this.buffalo.readUInt32(); + + return retries; } /** @@ -5271,17 +6381,18 @@ export class Ezsp extends EventEmitter { * @param param EmberBeaconClassificationParams * The beacon prioritization related variable * @returns The attempt to set the pramaters returns EMBER_SUCCESS */ - async ezspSetBeaconClassificationParams(param: EmberBeaconClassificationParams): Promise { + async ezspSetBeaconClassificationParams(param: EmberBeaconClassificationParams): Promise { this.startCommand(EzspFrameID.SET_BEACON_CLASSIFICATION_PARAMS); this.buffalo.writeEmberBeaconClassificationParams(param); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5290,21 +6401,91 @@ export class Ezsp extends EventEmitter { * @returns The attempt to get the pramaters returns EMBER_SUCCESS * @returns EmberBeaconClassificationParams * Gets the beacon prioritization related variable */ - async ezspGetBeaconClassificationParams(): Promise<[EmberStatus, param: EmberBeaconClassificationParams]> { + async ezspGetBeaconClassificationParams(): Promise<[SLStatus, param: EmberBeaconClassificationParams]> { this.startCommand(EzspFrameID.GET_BEACON_CLASSIFICATION_PARAMS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const param = this.buffalo.readEmberBeaconClassificationParams(); return [status, param]; } + /** + * Indicate whether there are pending messages in the APS retry queue. + * @returns True if there is a pending message for this network in the APS retry queue, false if not. + */ + async ezspPendingAckedMessages(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.PENDING_ACKED_MESSAGES); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const pendingMessages = this.buffalo.readUInt8() !== 0; + + return pendingMessages; + } + + /** + * Reschedule sending link status message, with first one being sent immediately. + * @returns + */ + async ezspRescheduleLinkStatusMsg(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.RESCHEDULE_LINK_STATUS_MSG); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * Set the network update ID to the desired value. Must be called before joining or forming the network. + * @param nwkUpdateId uint8_t Desired value of the network update ID. + * @param setWhenOnNetwork Set to true in case change should also apply when on network. + * @returns Status of set operation for the network update ID. + */ + async ezspSetNwkUpdateId(nwkUpdateId: number, setWhenOnNetwork: boolean): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_NWK_UPDATE_ID); + this.buffalo.writeUInt8(nwkUpdateId); + this.buffalo.writeUInt8(setWhenOnNetwork ? 1 : 0); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + //----------------------------------------------------------------------------- // Security Frames //----------------------------------------------------------------------------- @@ -5317,17 +6498,18 @@ export class Ezsp extends EventEmitter { * @param state EmberInitialSecurityState * The security configuration to be set. * @returns The success or failure code of the operation. */ - async ezspSetInitialSecurityState(state: EmberInitialSecurityState): Promise { + async ezspSetInitialSecurityState(state: EmberInitialSecurityState): Promise { this.startCommand(EzspFrameID.SET_INITIAL_SECURITY_STATE); this.buffalo.writeEmberInitialSecurityState(state); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5337,16 +6519,16 @@ export class Ezsp extends EventEmitter { * @returns The success or failure code of the operation. * @returns EmberCurrentSecurityState * The security configuration in use by the stack. */ - async ezspGetCurrentSecurityState(): Promise<[EmberStatus, state: EmberCurrentSecurityState]> { + async ezspGetCurrentSecurityState(): Promise<[SLStatus, state: EmberCurrentSecurityState]> { this.startCommand(EzspFrameID.GET_CURRENT_SECURITY_STATE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const state = this.buffalo.readEmberCurrentSecurityState(); return [status, state]; @@ -5358,7 +6540,7 @@ export class Ezsp extends EventEmitter { * @returns sl_zb_sec_man_key_t * Data to store the exported key in. * @returns sl_status_t * The success or failure code of the operation. */ - async ezspExportKey(context: SecManContext): Promise<[key: SecManKey, status: SLStatus]> { + async ezspExportKey(context: SecManContext): Promise<[SLStatus, key: SecManKey]> { /** * Export a key from storage. Certain keys are indexed, while others are not, as described here. * @@ -5400,23 +6582,31 @@ export class Ezsp extends EventEmitter { */ // NOTE: added for good measure if (context.coreKeyType === SecManKeyType.INTERNAL) { - logger.error(`ezspImportKey cannot use INTERNAL key type.`, NS); - return [null, SLStatus.INVALID_PARAMETER]; + logger.error(`ezspExportKey cannot use INTERNAL key type.`, NS); + throw new EzspError(EzspStatus.ERROR_INVALID_CALL); } this.startCommand(EzspFrameID.EXPORT_KEY); this.buffalo.writeSecManContext(context); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const key = this.buffalo.readSecManKey(); - const status: SLStatus = this.buffalo.readUInt32(); - return [key, status]; + if (this.version < 0x0E) { + const key = this.buffalo.readSecManKey(); + const status = this.buffalo.readUInt32(); + + return [status, key]; + } else { + const status = this.buffalo.readUInt32(); + const key = this.buffalo.readSecManKey(); + context = this.buffalo.readSecManContext(); + + return [status, key]; + } } /** @@ -5466,20 +6656,21 @@ export class Ezsp extends EventEmitter { // NOTE: added for good measure if (context.coreKeyType === SecManKeyType.INTERNAL) { logger.error(`ezspImportKey cannot use INTERNAL key type.`, NS); - return SLStatus.INVALID_PARAMETER; + throw new EzspError(EzspStatus.ERROR_INVALID_CALL); } this.startCommand(EzspFrameID.IMPORT_KEY); this.buffalo.writeSecManContext(context); this.buffalo.writeSecManKey(key); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: SLStatus = this.buffalo.readUInt32(); + + const status = this.buffalo.readUInt32(); + context = this.buffalo.readSecManContext(); return status; } @@ -5504,15 +6695,15 @@ export class Ezsp extends EventEmitter { * @returns uint8_t This indicates the index of the entry that matches the search * criteria. A value of 0xFF is returned if not matching entry is found. */ - async ezspFindKeyTableEntry(address: EmberEUI64, linkKey: boolean): Promise { + async ezspFindKeyTableEntry(address: EUI64, linkKey: boolean): Promise { this.startCommand(EzspFrameID.FIND_KEY_TABLE_ENTRY); this.buffalo.writeIeeeAddr(address); this.buffalo.writeUInt8(linkKey ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const index = this.buffalo.readUInt8(); @@ -5528,18 +6719,19 @@ export class Ezsp extends EventEmitter { * @param destinationEui64 The long address of the node to which this command will be sent * @returns An EmberStatus value indicating success of failure of the operation */ - async ezspSendTrustCenterLinkKey(destinationNodeId: EmberNodeId, destinationEui64: EmberEUI64): Promise { + async ezspSendTrustCenterLinkKey(destinationNodeId: NodeId, destinationEui64: EUI64): Promise { this.startCommand(EzspFrameID.SEND_TRUST_CENTER_LINK_KEY); this.buffalo.writeUInt16(destinationNodeId); this.buffalo.writeIeeeAddr(destinationEui64); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5550,17 +6742,18 @@ export class Ezsp extends EventEmitter { * @returns ::EMBER_SUCCESS if the index is valid and the key data was erased. * ::EMBER_KEY_INVALID if the index is out of range for the size of the key table. */ - async ezspEraseKeyTableEntry(index: number): Promise { + async ezspEraseKeyTableEntry(index: number): Promise { this.startCommand(EzspFrameID.ERASE_KEY_TABLE_ENTRY); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5569,16 +6762,17 @@ export class Ezsp extends EventEmitter { * @returns ::EMBER_SUCCESS if the key table was successfully cleared. * ::EMBER_INVALID_CALL otherwise. */ - async ezspClearKeyTable(): Promise { + async ezspClearKeyTable(): Promise { this.startCommand(EzspFrameID.CLEAR_KEY_TABLE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5601,17 +6795,18 @@ export class Ezsp extends EventEmitter { * @returns The success or failure of sending the request. * This is not the final result of the attempt. ezspZigbeeKeyEstablishmentHandler(...) will return that. */ - async ezspRequestLinkKey(partner: EmberEUI64): Promise { + async ezspRequestLinkKey(partner: EUI64): Promise { this.startCommand(EzspFrameID.REQUEST_LINK_KEY); this.buffalo.writeIeeeAddr(partner); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5627,17 +6822,18 @@ export class Ezsp extends EventEmitter { * If the Node Descriptor is successfully transmitted, ezspZigbeeKeyEstablishmentHandler(...) * will be called at a later time with a final status result. */ - async ezspUpdateTcLinkKey(maxAttempts: number): Promise { + async ezspUpdateTcLinkKey(maxAttempts: number): Promise { this.startCommand(EzspFrameID.UPDATE_TC_LINK_KEY); this.buffalo.writeUInt8(maxAttempts); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -5648,7 +6844,7 @@ export class Ezsp extends EventEmitter { * This value is all zeros on a failure. * @param status This is the status indicating what was established or why the key establishment failed. */ - ezspZigbeeKeyEstablishmentHandler(partner: EmberEUI64, status: EmberKeyStatus): void { + ezspZigbeeKeyEstablishmentHandler(partner: EUI64, status: EmberKeyStatus): void { logger.debug(`ezspZigbeeKeyEstablishmentHandler(): callback called with: [partner=${partner}], [status=${EmberKeyStatus[status]}]`, NS); // NOTE: For security reasons, any valid `partner` (not wildcard) that return with a status=TC_REQUESTER_VERIFY_KEY_TIMEOUT // are kicked off the network for posing a risk, unless HA devices allowed (as opposed to Z3) @@ -5661,10 +6857,10 @@ export class Ezsp extends EventEmitter { async ezspClearTransientLinkKeys(): Promise { this.startCommand(EzspFrameID.CLEAR_TRANSIENT_LINK_KEYS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -5687,13 +6883,13 @@ export class Ezsp extends EventEmitter { */ this.startCommand(EzspFrameID.GET_NETWORK_KEY_INFO); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: SLStatus = this.buffalo.readUInt32(); + const status = this.buffalo.readUInt32(); const networkKeyInfo = this.buffalo.readSecManNetworkKeyInfo(); return [status, networkKeyInfo]; @@ -5701,12 +6897,11 @@ export class Ezsp extends EventEmitter { /** * Retrieve metadata about an APS link key. Does not retrieve contents. - * @param context_in sl_zb_sec_man_context_t * Context used to input information about key. - * @returns EUI64 associated with this APS link key + * @param context sl_zb_sec_man_context_t * Context used to input information about key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the referenced key. * @returns sl_status_t * Status of metadata retrieval operation. */ - async ezspGetApsKeyInfo(context_in: SecManContext): Promise<[eui: EmberEUI64, key_data: SecManAPSKeyMetadata, status: SLStatus ]> { + async ezspGetApsKeyInfo(context: SecManContext): Promise<[status: SLStatus, keyData: SecManAPSKeyMetadata]> { /** * Retrieve metadata about an APS key. * It does not retrieve the actual key contents. @@ -5730,9 +6925,9 @@ export class Ezsp extends EventEmitter { * updated (e.g. a successful search by key_index will update the euii64 * field). * - * @param key_data sl_zb_sec_man_aps_key_metadata_t* [OUT] Metadata to fill in. + * @returns keyData sl_zb_sec_man_aps_key_metadata_t* [OUT] Metadata to fill in. * - * @return SL_STATUS_OK if successful, SL_STATUS_NOT_FOUND if + * @returns SL_STATUS_OK if successful, SL_STATUS_NOT_FOUND if * the key_index or eui64 does not result in a found entry, * SL_STATUS_INVALID_TYPE if the core key type is not an APS layer key (e.g. * SL_ZB_SEC_MAN_KEY_TYPE_NETWORK), or SL_STATUS_INVALID_MODE if core_key_type @@ -5743,19 +6938,27 @@ export class Ezsp extends EventEmitter { * initial security state). */ this.startCommand(EzspFrameID.GET_APS_KEY_INFO); - this.buffalo.writeSecManContext(context_in); + this.buffalo.writeSecManContext(context); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const eui: EmberEUI64 = this.buffalo.readIeeeAddr(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - return [eui, keyData, status]; + if (this.version < 0x0E) { + context.eui64 = this.buffalo.readIeeeAddr(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + const status = this.buffalo.readUInt32(); + + return [status, keyData]; + } else { + const status = this.buffalo.readUInt32(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + context = this.buffalo.readSecManContext(); + + return [status, keyData]; + } } /** @@ -5765,7 +6968,7 @@ export class Ezsp extends EventEmitter { * @param plaintextKey sl_zb_sec_man_key_t * The key data to be imported. * @returns Status of key import operation. */ - async ezspImportLinkKey(index: number, address: EmberEUI64, plaintextKey: SecManKey): Promise { + async ezspImportLinkKey(index: number, address: EUI64, plaintextKey: SecManKey): Promise { /** * Import a link key, or SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK key, into storage. * @@ -5774,7 +6977,7 @@ export class Ezsp extends EventEmitter { * the key will either overwrite whichever key table entry has an EUI of address * (if one exists) or write to the first available key table entry. The index * that the key was placed into will not be returned by this API. - * @param address EmberEUI64 [IN] The EUI belonging to the key. + * @param address EUI64 [IN] The EUI belonging to the key. * @param plaintext_key sl_zb_sec_man_key_t* [IN] A pointer to the key to import. * * @return SL_STATUS_OK upon success, a valid error code otherwise. @@ -5785,67 +6988,79 @@ export class Ezsp extends EventEmitter { this.buffalo.writeIeeeAddr(address); this.buffalo.writeSecManKey(plaintextKey); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: SLStatus = this.buffalo.readUInt32(); + + const status = this.buffalo.readUInt32(); + return status; } /** * Export the link key at given index from the key table. * @param uint8_t Index of key to export. - * @returns EUI64 associated with the exported key. + * @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with. * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. * @returns sl_status_t * Status of key export operation. */ - async ezspExportLinkKeyByIndex(index: number): - Promise<[eui: EmberEUI64, plaintextKey: SecManKey, keyData: SecManAPSKeyMetadata, status: SLStatus]> { + async ezspExportLinkKeyByIndex(index: number) + : Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, keyData: SecManAPSKeyMetadata]> { /** * Export an APS link key by index. * * @param index uint8_t - * @param address EmberEUI64 + * @param address EUI64 * @param plaintext_key sl_zb_sec_man_key_t* * @param key_data sl_zb_sec_man_aps_key_metadata_t* */ this.startCommand(EzspFrameID.EXPORT_LINK_KEY_BY_INDEX); this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const eui: EmberEUI64 = this.buffalo.readIeeeAddr(); - const plaintextKey: SecManKey = this.buffalo.readSecManKey(); - const keyData: SecManAPSKeyMetadata = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - return [eui, plaintextKey, keyData, status]; + if (this.version < 0x0E) { + const context = initSecurityManagerContext(); + context.coreKeyType = SecManKeyType.APP_LINK; + context.keyIndex = index; + context.eui64 = this.buffalo.readIeeeAddr(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + const status = this.buffalo.readUInt32(); + + return [status, , plaintextKey, keyData]; + } else { + const status = this.buffalo.readUInt32(); + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + + return [status, context, plaintextKey, keyData]; + } } /** * Export the link key associated with the given EUI from the key table. * @param eui EUI64 associated with the key to export. * @returns sl_zb_sec_man_key_t * The exported key. - * @returns uint8_t * Key index of the exported key. + * @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. * @returns sl_status_t * Status of key export operation. */ - async ezspExportLinkKeyByEui(eui: EmberEUI64): - Promise<[plaintextKey: SecManKey, index: number, keyData: SecManAPSKeyMetadata, status: SLStatus]> { + async ezspExportLinkKeyByEui(eui: EUI64): Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, keyData: SecManAPSKeyMetadata]> { /** * Search through the Key table to find an entry that has the same EUI address as the passed value. * If NULL is passed in for the address then it finds the first unused entry and sets the index in the context. * It is valid to pass in NULL to plaintext_key or key_data in case the index of the referenced key is desired * but not its value or other metadata. - * @param eui EmberEUI64 + * @param eui EUI64 * @param context sl_zb_sec_man_context_t* * @param plaintext_key sl_zb_sec_man_key_t* * @param key_data sl_zb_sec_man_aps_key_metadata_t* @@ -5853,18 +7068,30 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.EXPORT_LINK_KEY_BY_EUI); this.buffalo.writeIeeeAddr(eui); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const plaintextKey: SecManKey = this.buffalo.readSecManKey(); - const index = this.buffalo.readUInt8(); - const keyData: SecManAPSKeyMetadata = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - return [plaintextKey, index, keyData, status]; + if (this.version < 0x0E) { + const plaintextKey = this.buffalo.readSecManKey(); + const context = initSecurityManagerContext(); + context.coreKeyType = SecManKeyType.APP_LINK; + context.keyIndex = this.buffalo.readUInt8(); + context.eui64 = eui; + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + const status = this.buffalo.readUInt32(); + + return [status, context, plaintextKey, keyData]; + } else { + const status = this.buffalo.readUInt32(); + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + + return [status, context, plaintextKey, keyData]; + } } /** @@ -5885,13 +7112,14 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.CHECK_KEY_CONTEXT); this.buffalo.writeSecManContext(context); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: SLStatus = this.buffalo.readUInt32(); + + const status = this.buffalo.readUInt32(); + return status; } @@ -5899,10 +7127,10 @@ export class Ezsp extends EventEmitter { * Import a transient link key. * @param eui64 EUI64 associated with this transient key. * @param plaintextKey sl_zb_sec_man_key_t * The key to import. - * @param sl_zigbee_sec_man_flags_t Flags associated with this transient key. + * @param flags sl_zigbee_sec_man_flags_t (v13-) Flags associated with this transient key. * @returns Status of key import operation. */ - async ezspImportTransientKey(eui64: EmberEUI64, plaintextKey: SecManKey, flags: SecManFlag): Promise { + async ezspImportTransientKey(eui64: EUI64, plaintextKey: SecManKey, flags: SecManFlag = SecManFlag.NONE): Promise { /** * @brief Add a transient or temporary key entry to key storage. * A key entry added with this API is timed out after @@ -5911,25 +7139,22 @@ export class Ezsp extends EventEmitter { * after the longer between * ::EMBER_AF_PLUGIN_NETWORK_CREATOR_SECURITY_NETWORK_OPEN_TIME_S seconds and * ::EMBER_TRANSIENT_KEY_TIMEOUT_S seconds. - * - * @param eui64 [IN] An EmberEUI64 to import. - * @param plaintext_key [IN] A sl_zb_sec_man_key_t* to import. - * @param flags [IN] - * - * @return See ::zb_sec_man_import_transient_key for return information. */ this.startCommand(EzspFrameID.IMPORT_TRANSIENT_KEY); this.buffalo.writeIeeeAddr(eui64); this.buffalo.writeSecManKey(plaintextKey); - this.buffalo.writeUInt8(flags); - - const sendStatus: EzspStatus = await this.sendCommand(); + + if (this.version < 0x0E) { + this.buffalo.writeUInt8(flags); + } + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: SLStatus = this.buffalo.readUInt32(); + + const status = this.buffalo.readUInt32(); return status; } @@ -5941,36 +7166,35 @@ export class Ezsp extends EventEmitter { * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. * @returns sl_status_t * Status of key export operation. + * - SL_STATUS_OK upon success + * - SL_STATUS_NOT_FOUND otherwise. */ - async ezspExportTransientKeyByIndex(index: number): - Promise<[context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata, status: SLStatus]> { - /** - * Search for a transient, or temporary, key - * entry from key storage by key index. - * - * @param index [IN] The key_index to fetch. - * @param context sl_zb_sec_man_context_t* [OUT] The context about the key, filled in upon success. - * @param plaintext_key sl_zb_sec_man_key_t* [OUT] If the security configuration allows for it, filled in - * with the key contents upon success. - * @param key_data sl_zb_sec_man_aps_key_metadata_t* [OUT] Filled in with metadata about the key upon success. - * - * @return sl_status_t SL_STATUS_OK upon success, SL_STATUS_NOT_FOUND otherwise. - */ + async ezspExportTransientKeyByIndex(index: number) + : Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata]> { this.startCommand(EzspFrameID.EXPORT_TRANSIENT_KEY_BY_INDEX); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const context = this.buffalo.readSecManContext(); - const plaintextKey = this.buffalo.readSecManKey(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); + if (this.version < 0x0E) { + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + const status = this.buffalo.readUInt32(); + + return [status, context, plaintextKey, keyData]; + } else { + const status = this.buffalo.readUInt32(); + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); - return [context, plaintextKey, keyData, status]; + return [status, context, plaintextKey, keyData]; + } } /** @@ -5980,38 +7204,90 @@ export class Ezsp extends EventEmitter { * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. * @returns sl_status_t * Status of key export operation. + * - SL_STATUS_OK upon success + * - SL_STATUS_NOT_FOUND otherwise. */ - async ezspExportTransientKeyByEui(eui: EmberEUI64): - Promise<[context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata, status: SLStatus]> { - /** - * Search for a transient, or temporary, key - * entry from key storage by EUI. - * - * @param eui64 [IN] The EUI to search for. - * @param context sl_zb_sec_man_context_t* [OUT] The context about the key, filled in upon success. - * @param plaintext_key sl_zb_sec_man_key_t* [OUT] If the security configuration allows for it, filled in - * with the key contents upon success. - * @param key_data sl_zb_sec_man_aps_key_metadata_t* [OUT] Filled in with metadata about the key upon success. - * - * @return sl_status_t SL_STATUS_OK upon success, SL_STATUS_NOT_FOUND otherwise. - */ + async ezspExportTransientKeyByEui(eui: EUI64): + Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata]> { this.startCommand(EzspFrameID.EXPORT_TRANSIENT_KEY_BY_EUI); this.buffalo.writeIeeeAddr(eui); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const context = this.buffalo.readSecManContext(); - const plaintextKey = this.buffalo.readSecManKey(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); + if (this.version < 0x0E) { + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + const status = this.buffalo.readUInt32(); + + return [status, context, plaintextKey, keyData]; + } else { + const status = this.buffalo.readUInt32(); + const context = this.buffalo.readSecManContext(); + const plaintextKey = this.buffalo.readSecManKey(); + const keyData = this.buffalo.readSecManAPSKeyMetadata(); + + return [status, context, plaintextKey, keyData]; + } + } + + /** + * Set the incoming TC link key frame counter to desired value. + * @param frameCounter Value to set the frame counter to. + */ + async ezspSetIncomingTcLinkKeyFrameCounter(frameCounter: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.SET_INCOMING_TC_LINK_KEY_FRAME_COUNTER); + this.buffalo.writeUInt32(frameCounter); + + const sendStatus = await this.sendCommand(); - return [context, plaintextKey, keyData, status]; + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } } + /** + * Encrypt/decrypt a message in-place using APS. + * @param encrypt Encrypt (true) or decrypt (false) the message. + * @param lengthCombinedArg uint8_t Length of the array containing message, needs to be long enough to include the auxiliary header and MIC. + * @param message uint8_t * The message to be en/de-crypted. + * @param apsHeaderEndIndex uint8_t Index just past the APS frame. + * @param remoteEui64 IEEE address of the device this message is associated with. + * @returns Status of the encryption/decryption call. + */ + async ezspApsCryptMessage(encrypt: boolean, lengthCombinedArg: number, message: Buffer, apsHeaderEndIndex: number, remoteEui64: EUI64) + : Promise<[SLStatus, cryptedMessage: Buffer]> { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.APS_CRYPT_MESSAGE); + this.buffalo.writeUInt8(encrypt ? 1 : 0); + this.buffalo.writeUInt8(lengthCombinedArg); + this.buffalo.writeBuffer(message, lengthCombinedArg); + this.buffalo.writeUInt8(apsHeaderEndIndex); + this.buffalo.writeIeeeAddr(remoteEui64); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + const cryptedMessage = this.buffalo.readBuffer(lengthCombinedArg); + + return [status, cryptedMessage]; + } + //----------------------------------------------------------------------------- // Trust Center Frames //----------------------------------------------------------------------------- @@ -6028,8 +7304,8 @@ export class Ezsp extends EventEmitter { * @param policyDecision An EmberJoinDecision reflecting the decision made. * @param parentOfNewNodeId The parent of the node whose status has changed. */ - ezspTrustCenterJoinHandler(newNodeId: EmberNodeId, newNodeEui64: EmberEUI64, status: EmberDeviceUpdate, - policyDecision: EmberJoinDecision, parentOfNewNodeId: EmberNodeId): void { + ezspTrustCenterJoinHandler(newNodeId: NodeId, newNodeEui64: EUI64, status: EmberDeviceUpdate, + policyDecision: EmberJoinDecision, parentOfNewNodeId: NodeId): void { logger.debug(`ezspTrustCenterJoinHandler(): callback called with: [newNodeId=${newNodeId}], [newNodeEui64=${newNodeEui64}], ` + `[status=${EmberDeviceUpdate[status]}], [policyDecision=${EmberJoinDecision[policyDecision]}], ` + `[parentOfNewNodeId=${parentOfNewNodeId}]`, NS); @@ -6047,17 +7323,18 @@ export class Ezsp extends EventEmitter { * An all zero key may be passed in, which will cause the stack to randomly generate a new key. * @returns EmberStatus value that indicates the success or failure of the command. */ - async ezspBroadcastNextNetworkKey(key: EmberKeyData): Promise { + async ezspBroadcastNextNetworkKey(key: EmberKeyData): Promise { this.startCommand(EzspFrameID.BROADCAST_NEXT_NETWORK_KEY); this.buffalo.writeEmberKeyData(key); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6067,16 +7344,17 @@ export class Ezsp extends EventEmitter { * @returns EmberStatus value that indicates the success or failure of the * command. */ - async ezspBroadcastNetworkKeySwitch(): Promise { + async ezspBroadcastNetworkKeySwitch(): Promise { this.startCommand(EzspFrameID.BROADCAST_NETWORK_KEY_SWITCH); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6093,19 +7371,19 @@ export class Ezsp extends EventEmitter { * @returns EmberAesMmoHashContext * The updated hash context. */ async ezspAesMmoHash(context: EmberAesMmoHashContext, finalize: boolean, data: Buffer): - Promise<[EmberStatus, returnContext: EmberAesMmoHashContext]> { + Promise<[SLStatus, returnContext: EmberAesMmoHashContext]> { this.startCommand(EzspFrameID.AES_MMO_HASH); this.buffalo.writeEmberAesMmoHashContext(context); this.buffalo.writeUInt8(finalize ? 1 : 0); this.buffalo.writePayload(data); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const returnContext = this.buffalo.readEmberAesMmoHashContext(); return [status, returnContext]; @@ -6120,19 +7398,20 @@ export class Ezsp extends EventEmitter { * @param targetLong The long address (EUI64) of the device to be removed. * @returns An EmberStatus value indicating success, or the reason for failure */ - async ezspRemoveDevice(destShort: EmberNodeId, destLong: EmberEUI64, targetLong: EmberEUI64): Promise { + async ezspRemoveDevice(destShort: NodeId, destLong: EUI64, targetLong: EUI64): Promise { this.startCommand(EzspFrameID.REMOVE_DEVICE); this.buffalo.writeUInt16(destShort); this.buffalo.writeIeeeAddr(destLong); this.buffalo.writeIeeeAddr(targetLong); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6145,19 +7424,20 @@ export class Ezsp extends EventEmitter { * @param key EmberKeyData * The NWK key to send to the new device. * @returns An EmberStatus value indicating success, or the reason for failure */ - async ezspUnicastNwkKeyUpdate(destShort: EmberNodeId, destLong: EmberEUI64, key: EmberKeyData): Promise { + async ezspUnicastNwkKeyUpdate(destShort: NodeId, destLong: EUI64, key: EmberKeyData): Promise { this.startCommand(EzspFrameID.UNICAST_NWK_KEY_UPDATE); this.buffalo.writeUInt16(destShort); this.buffalo.writeIeeeAddr(destLong); this.buffalo.writeEmberKeyData(key); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6170,16 +7450,17 @@ export class Ezsp extends EventEmitter { * When complete it stores the private key. The results are returned via * ezspGenerateCbkeKeysHandler(). */ - async ezspGenerateCbkeKeys(): Promise { + async ezspGenerateCbkeKeys(): Promise { this.startCommand(EzspFrameID.GENERATE_CBKE_KEYS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6192,9 +7473,9 @@ export class Ezsp extends EventEmitter { * @param status The result of the CBKE operation. * @param ephemeralPublicKey EmberPublicKeyData * The generated ephemeral public key. */ - ezspGenerateCbkeKeysHandler(status: EmberStatus, ephemeralPublicKey: EmberPublicKeyData): void { + ezspGenerateCbkeKeysHandler(status: SLStatus, ephemeralPublicKey: EmberPublicKeyData): void { logger.debug( - `ezspGenerateCbkeKeysHandler(): callback called with: [status=${EmberStatus[status]}], [ephemeralPublicKey=${ephemeralPublicKey}]`, + `ezspGenerateCbkeKeysHandler(): callback called with: [status=${SLStatus[status]}], [ephemeralPublicKey=${ephemeralPublicKey}]`, NS, ); } @@ -6210,19 +7491,20 @@ export class Ezsp extends EventEmitter { * @param partnerEphemeralPublicKey EmberPublicKeyData * The key establishment partner's ephemeral public key */ async ezspCalculateSmacs(amInitiator: boolean, partnerCertificate: EmberCertificateData, partnerEphemeralPublicKey: EmberPublicKeyData) - : Promise { + : Promise { this.startCommand(EzspFrameID.CALCULATE_SMACS); this.buffalo.writeUInt8(amInitiator ? 1 : 0); this.buffalo.writeEmberCertificateData(partnerCertificate); this.buffalo.writeEmberPublicKeyData(partnerEphemeralPublicKey); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6236,8 +7518,8 @@ export class Ezsp extends EventEmitter { * @param initiatorSmac EmberSmacData * The calculated value of the initiator's SMAC * @param responderSmac EmberSmacData * The calculated value of the responder's SMAC */ - ezspCalculateSmacsHandler(status: EmberStatus, initiatorSmac: EmberSmacData, responderSmac: EmberSmacData): void { - logger.debug(`ezspCalculateSmacsHandler(): callback called with: [status=${EmberStatus[status]}], [initiatorSmac=${initiatorSmac}], ` + ezspCalculateSmacsHandler(status: SLStatus, initiatorSmac: EmberSmacData, responderSmac: EmberSmacData): void { + logger.debug(`ezspCalculateSmacsHandler(): callback called with: [status=${SLStatus[status]}], [initiatorSmac=${initiatorSmac}], ` + `[responderSmac=${responderSmac}]`, NS); } @@ -6246,16 +7528,17 @@ export class Ezsp extends EventEmitter { * Public/Private key pair. When complete it stores the private key. The results * are returned via ezspGenerateCbkeKeysHandler283k1(). */ - async ezspGenerateCbkeKeys283k1(): Promise { + async ezspGenerateCbkeKeys283k1(): Promise { this.startCommand(EzspFrameID.GENERATE_CBKE_KEYS283K1); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6268,8 +7551,8 @@ export class Ezsp extends EventEmitter { * @param status The result of the CBKE operation. * @param ephemeralPublicKey EmberPublicKey283k1Data * The generated ephemeral public key. */ - ezspGenerateCbkeKeysHandler283k1(status: EmberStatus, ephemeralPublicKey: EmberPublicKey283k1Data): void { - logger.debug(`ezspGenerateCbkeKeysHandler283k1(): callback called with: [status=${EmberStatus[status]}], ` + ezspGenerateCbkeKeysHandler283k1(status: SLStatus, ephemeralPublicKey: EmberPublicKey283k1Data): void { + logger.debug(`ezspGenerateCbkeKeysHandler283k1(): callback called with: [status=${SLStatus[status]}], ` + `[ephemeralPublicKey=${ephemeralPublicKey}]`, NS); } @@ -6284,19 +7567,20 @@ export class Ezsp extends EventEmitter { * @param partnerEphemeralPublicKey EmberPublicKey283k1Data * The key establishment partner's ephemeral public key */ async ezspCalculateSmacs283k1(amInitiator: boolean, partnerCertificate: EmberCertificate283k1Data, - partnerEphemeralPublicKey: EmberPublicKey283k1Data): Promise { + partnerEphemeralPublicKey: EmberPublicKey283k1Data): Promise { this.startCommand(EzspFrameID.CALCULATE_SMACS283K1); this.buffalo.writeUInt8(amInitiator ? 1 : 0); this.buffalo.writeEmberCertificate283k1Data(partnerCertificate); this.buffalo.writeEmberPublicKey283k1Data(partnerEphemeralPublicKey); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6311,8 +7595,8 @@ export class Ezsp extends EventEmitter { * @param initiatorSmac EmberSmacData * The calculated value of the initiator's SMAC * @param responderSmac EmberSmacData * The calculated value of the responder's SMAC */ - ezspCalculateSmacsHandler283k1(status: EmberStatus, initiatorSmac: EmberSmacData, responderSmac: EmberSmacData): void { - logger.debug(`ezspCalculateSmacsHandler283k1(): callback called with: [status=${EmberStatus[status]}], [initiatorSmac=${initiatorSmac}], ` + ezspCalculateSmacsHandler283k1(status: SLStatus, initiatorSmac: EmberSmacData, responderSmac: EmberSmacData): void { + logger.debug(`ezspCalculateSmacsHandler283k1(): callback called with: [status=${SLStatus[status]}], [initiatorSmac=${initiatorSmac}], ` + `[responderSmac=${responderSmac}]`, NS); } @@ -6324,17 +7608,18 @@ export class Ezsp extends EventEmitter { * @param storeLinkKey A bool indicating whether to store (true) or discard (false) the unverified link * key derived when ezspCalculateSmacs() was previously called. */ - async ezspClearTemporaryDataMaybeStoreLinkKey(storeLinkKey: boolean): Promise { + async ezspClearTemporaryDataMaybeStoreLinkKey(storeLinkKey: boolean): Promise { this.startCommand(EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY); this.buffalo.writeUInt8(storeLinkKey ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6346,17 +7631,18 @@ export class Ezsp extends EventEmitter { * @param storeLinkKey A bool indicating whether to store (true) or discard (false) the unverified link * key derived when ezspCalculateSmacs() was previously called. */ - async ezspClearTemporaryDataMaybeStoreLinkKey283k1(storeLinkKey: boolean): Promise { + async ezspClearTemporaryDataMaybeStoreLinkKey283k1(storeLinkKey: boolean): Promise { this.startCommand(EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1); this.buffalo.writeUInt8(storeLinkKey ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6364,16 +7650,16 @@ export class Ezsp extends EventEmitter { * Retrieves the certificate installed on the NCP. * @returns EmberCertificateData * The locally installed certificate. */ - async ezspGetCertificate(): Promise<[EmberStatus, localCert: EmberCertificateData]> { + async ezspGetCertificate(): Promise<[SLStatus, localCert: EmberCertificateData]> { this.startCommand(EzspFrameID.GET_CERTIFICATE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); const localCert = this.buffalo.readEmberCertificateData(); return [status, localCert]; @@ -6383,16 +7669,16 @@ export class Ezsp extends EventEmitter { * Retrieves the 283k certificate installed on the NCP. * @returns EmberCertificate283k1Data * The locally installed certificate. */ - async ezspGetCertificate283k1(): Promise<[EmberStatus, localCert: EmberCertificate283k1Data]> { + async ezspGetCertificate283k1(): Promise<[SLStatus, localCert: EmberCertificate283k1Data]> { this.startCommand(EzspFrameID.GET_CERTIFICATE283K1); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const localCert = this.buffalo.readEmberCertificate283k1Data(); return [status, localCert]; @@ -6433,17 +7719,18 @@ export class Ezsp extends EventEmitter { * for execution. EMBER_INVALID_CALL if the operation can't be performed in this * context, possibly because another ECC operation is pending. */ - async ezspDsaSign(messageContents: Buffer): Promise { + async ezspDsaSign(messageContents: Buffer): Promise { this.startCommand(EzspFrameID.DSA_SIGN); this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6457,9 +7744,9 @@ export class Ezsp extends EventEmitter { * @param messageLength uint8_t The length of the messageContents parameter in bytes. * @param messageContents uint8_t *The message and attached which includes the original message and the appended signature. */ - ezspDsaSignHandler(status: EmberStatus, messageContents: Buffer): void { + ezspDsaSignHandler(status: SLStatus, messageContents: Buffer): void { logger.debug( - `ezspDsaSignHandler(): callback called with: [status=${EmberStatus[status]}], [messageContents=${messageContents.toString('hex')}]`, + `ezspDsaSignHandler(): callback called with: [status=${SLStatus[status]}], [messageContents=${messageContents.toString('hex')}]`, NS, ); } @@ -6474,19 +7761,20 @@ export class Ezsp extends EventEmitter { * certificate must both be issued by the same Certificate Authority, so they should share the same CA Public Key. * @param receivedSig EmberSignatureData * The signature of the signed data. */ - async ezspDsaVerify(digest: EmberMessageDigest, signerCertificate: EmberCertificateData, receivedSig: EmberSignatureData): Promise { + async ezspDsaVerify(digest: EmberMessageDigest, signerCertificate: EmberCertificateData, receivedSig: EmberSignatureData): Promise { this.startCommand(EzspFrameID.DSA_VERIFY); this.buffalo.writeEmberMessageDigest(digest); this.buffalo.writeEmberCertificateData(signerCertificate); this.buffalo.writeEmberSignatureData(receivedSig); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6499,8 +7787,8 @@ export class Ezsp extends EventEmitter { * failed and the validity is unknown. * @param status The result of the DSA verification operation. */ - ezspDsaVerifyHandler(status: EmberStatus): void { - logger.debug(`ezspDsaVerifyHandler(): callback called with: [status=${EmberStatus[status]}]`, NS); + ezspDsaVerifyHandler(status: SLStatus): void { + logger.debug(`ezspDsaVerifyHandler(): callback called with: [status=${SLStatus[status]}]`, NS); } /** @@ -6514,19 +7802,20 @@ export class Ezsp extends EventEmitter { * @param receivedSig EmberSignature283k1Data * The signature of the signed data. */ async ezspDsaVerify283k1(digest: EmberMessageDigest, signerCertificate: EmberCertificate283k1Data, receivedSig: EmberSignature283k1Data) - : Promise { + : Promise { this.startCommand(EzspFrameID.DSA_VERIFY283K1); this.buffalo.writeEmberMessageDigest(digest); this.buffalo.writeEmberCertificate283k1Data(signerCertificate); this.buffalo.writeEmberSignature283k1Data(receivedSig); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6538,19 +7827,20 @@ export class Ezsp extends EventEmitter { * @param myKey EmberPrivateKeyData *The node's new static private key. */ async ezspSetPreinstalledCbkeData(caPublic: EmberPublicKeyData, myCert: EmberCertificateData, myKey: EmberPrivateKeyData): - Promise { + Promise { this.startCommand(EzspFrameID.SET_PREINSTALLED_CBKE_DATA); this.buffalo.writeEmberPublicKeyData(caPublic); this.buffalo.writeEmberCertificateData(myCert); this.buffalo.writeEmberPrivateKeyData(myKey); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6559,16 +7849,17 @@ export class Ezsp extends EventEmitter { * private key on the NCP associated with this node. * @returns Status of operation */ - async ezspSavePreinstalledCbkeData283k1(): Promise { + async ezspSavePreinstalledCbkeData283k1(): Promise { this.startCommand(EzspFrameID.SAVE_PREINSTALLED_CBKE_DATA283K1); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6584,17 +7875,18 @@ export class Ezsp extends EventEmitter { * @param rxCallback true to generate a mfglibRxHandler callback when a packet is received. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspMfglibStart(rxCallback: boolean): Promise { - this.startCommand(EzspFrameID.MFGLIB_START); + async mfglibInternalStart(rxCallback: boolean): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START); this.buffalo.writeUInt8(rxCallback ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6604,16 +7896,17 @@ export class Ezsp extends EventEmitter { * mfglibStart() at the same time. * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibEnd(): Promise { - this.startCommand(EzspFrameID.MFGLIB_END); + async mfglibInternalEnd(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_END); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6625,16 +7918,17 @@ export class Ezsp extends EventEmitter { * etc.) * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibStartTone(): Promise { - this.startCommand(EzspFrameID.MFGLIB_START_TONE); + async mfglibInternalStartTone(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START_TONE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6642,16 +7936,17 @@ export class Ezsp extends EventEmitter { * Stops transmitting tone started by mfglibStartTone(). * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibStopTone(): Promise { - this.startCommand(EzspFrameID.MFGLIB_STOP_TONE); + async mfglibInternalStopTone(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_STOP_TONE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6660,16 +7955,17 @@ export class Ezsp extends EventEmitter { * modulation can be measured. * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibStartStream(): Promise { - this.startCommand(EzspFrameID.MFGLIB_START_STREAM); + async mfglibInternalStartStream(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START_STREAM); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6678,16 +7974,17 @@ export class Ezsp extends EventEmitter { * mfglibStartStream(). * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibStopStream(): Promise { - this.startCommand(EzspFrameID.MFGLIB_STOP_STREAM); + async mfglibInternalStopStream(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_STOP_STREAM); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6700,17 +7997,18 @@ export class Ezsp extends EventEmitter { * @param packetContents uint8_t * The packet to send. The last two bytes will be replaced with the 16-bit CRC. * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibSendPacket(packetContents: Buffer): Promise { - this.startCommand(EzspFrameID.MFGLIB_SEND_PACKET); + async mfglibInternalSendPacket(packetContents: Buffer): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SEND_PACKET); this.buffalo.writePayload(packetContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6720,17 +8018,18 @@ export class Ezsp extends EventEmitter { * @param channel uint8_t The channel to switch to. Valid values are 11 - 26. * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibSetChannel(channel: number): Promise { - this.startCommand(EzspFrameID.MFGLIB_SET_CHANNEL); + async mfglibInternalSetChannel(channel: number): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SET_CHANNEL); this.buffalo.writeUInt8(channel); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } + + const status = this.buffalo.readStatus(this.version); - const status: EmberStatus = this.buffalo.readUInt8(); return status; } @@ -6738,16 +8037,17 @@ export class Ezsp extends EventEmitter { * Returns the current radio channel, as previously set via mfglibSetChannel(). * @returns uint8_t The current channel. */ - async mfglibGetChannel(): Promise { - this.startCommand(EzspFrameID.MFGLIB_GET_CHANNEL); + async mfglibInternalGetChannel(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_GET_CHANNEL); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const channel = this.buffalo.readUInt8(); + return channel; } @@ -6761,18 +8061,19 @@ export class Ezsp extends EventEmitter { * @param power int8_t Power in units of dBm. Refer to radio data sheet for valid range. * @returns An EmberStatus value indicating success or the reason for failure. */ - async mfglibSetPower(txPowerMode: EmberTXPowerMode, power: number): Promise { - this.startCommand(EzspFrameID.MFGLIB_SET_POWER); + async mfglibInternalSetPower(txPowerMode: EmberTXPowerMode, power: number): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SET_POWER); this.buffalo.writeUInt16(txPowerMode); this.buffalo.writeInt8(power); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6780,16 +8081,17 @@ export class Ezsp extends EventEmitter { * Returns the current radio power setting, as previously set via mfglibSetPower(). * @returns int8_t Power in units of dBm. Refer to radio data sheet for valid range. */ - async mfglibGetPower(): Promise { - this.startCommand(EzspFrameID.MFGLIB_GET_POWER); - - const sendStatus: EzspStatus = await this.sendCommand(); + async mfglibInternalGetPower(): Promise { + this.startCommand(EzspFrameID.MFGLIB_INTERNAL_GET_POWER); + + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - + const power = this.buffalo.readInt8(); + return power; } @@ -6821,17 +8123,18 @@ export class Ezsp extends EventEmitter { * with XMODEM over the serial protocol's Bootloader Frame. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspLaunchStandaloneBootloader(mode: number): Promise { + async ezspLaunchStandaloneBootloader(mode: number): Promise { this.startCommand(EzspFrameID.LAUNCH_STANDALONE_BOOTLOADER); this.buffalo.writeUInt8(mode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6845,20 +8148,20 @@ export class Ezsp extends EventEmitter { * @param messageContents uint8_t * The multicast message. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSendBootloadMessage(broadcast: boolean, destEui64: EmberEUI64, messageContents: Buffer): - Promise { + async ezspSendBootloadMessage(broadcast: boolean, destEui64: EUI64, messageContents: Buffer): Promise { this.startCommand(EzspFrameID.SEND_BOOTLOAD_MESSAGE); this.buffalo.writeUInt8(broadcast ? 1 : 0); this.buffalo.writeIeeeAddr(destEui64); this.buffalo.writePayload(messageContents); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6873,21 +8176,22 @@ export class Ezsp extends EventEmitter { * @returns uint8_t * The value of MICRO on the node * @returns uint8_t * The value of PHY on the node */ - async ezspGetStandaloneBootloaderVersionPlatMicroPhy(): Promise<[number, nodePlat: number, nodeMicro: number, nodePhy: number]> { + async ezspGetStandaloneBootloaderVersionPlatMicroPhy() + : Promise<[bootloaderVersion: number, nodePlat: number, nodeMicro: number, nodePhy: number]> { this.startCommand(EzspFrameID.GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const bootloader_version = this.buffalo.readUInt16(); + const bootloaderVersion = this.buffalo.readUInt16(); const nodePlat = this.buffalo.readUInt8(); const nodeMicro = this.buffalo.readUInt8(); const nodePhy = this.buffalo.readUInt8(); - return [bootloader_version, nodePlat, nodeMicro, nodePhy]; + return [bootloaderVersion, nodePlat, nodeMicro, nodePhy]; } /** @@ -6895,27 +8199,25 @@ export class Ezsp extends EventEmitter { * A callback invoked by the EmberZNet stack when a bootload message is * received. * @param longId The EUI64 of the sending node. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. + * @param packetInfo Information about the incoming packet. * @param messageContents uint8_t *The bootload message that was sent. */ - ezspIncomingBootloadMessageHandler(longId: EmberEUI64, lastHopLqi: number, lastHopRssi: number, messageContents: Buffer): void { - logger.debug(`ezspIncomingBootloadMessageHandler(): callback called with: [longId=${longId}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}], [messageContents=${messageContents.toString('hex')}]`, NS); + ezspIncomingBootloadMessageHandler(longId: EUI64, packetInfo: EmberRxPacketInfo, messageContents: Buffer): void { + logger.debug(`ezspIncomingBootloadMessageHandler(): callback called with: [longId=${longId}], [packetInfo=${JSON.stringify(packetInfo)}], ` + + `[messageContents=${messageContents.toString('hex')}]`, NS); } /** * Callback * A callback invoked by the EmberZNet stack when the MAC has finished * transmitting a bootload message. - * @param status An EmberStatus value of EMBER_SUCCESS if an ACK was received from the destination - * or EMBER_DELIVERY_FAILED if no ACK was received. + * @param status An EmberStatus value of SL_STATUS_OK if an ACK was received from the destination + * or SL_STATUS_ZIGBEE_DELIVERY_FAILED if no ACK was received. * @param messageLength uint8_t The length of the messageContents parameter in bytes. * @param messageContents uint8_t * The message that was sent. */ - ezspBootloadTransmitCompleteHandler(status: EmberStatus, messageContents: Buffer): void { - logger.debug(`ezspBootloadTransmitCompleteHandler(): callback called with: [status=${EmberStatus[status]}], ` + ezspBootloadTransmitCompleteHandler(status: SLStatus, messageContents: Buffer): void { + logger.debug(`ezspBootloadTransmitCompleteHandler(): callback called with: [status=${SLStatus[status]}], ` + `[messageContents=${messageContents.toString('hex')}]`, NS); } @@ -6930,10 +8232,10 @@ export class Ezsp extends EventEmitter { this.buffalo.writeListUInt8(plaintext); this.buffalo.writeListUInt8(key); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const ciphertext = this.buffalo.readListUInt8(EMBER_ENCRYPTION_KEY_SIZE); @@ -6941,6 +8243,181 @@ export class Ezsp extends EventEmitter { return ciphertext; } + + /** + * Callback + * A callback to be implemented on the Golden Node to process acknowledgements. + * If you supply a custom version of this handler, you must define SL_ZIGBEE_APPLICATION_HAS_INCOMING_MFG_TEST_MESSAGE_HANDLER + * in your application's CONFIGURATION_HEADER + * @param messageType uint8_t The type of the incoming message. Currently, the only possibility is MFG_TEST_TYPE_ACK. + * @param data uint8_t * A pointer to the data received in the current message. + */ + ezspIncomingMfgTestMessageHandler(messageType: number, messageContents: Buffer): void { + logger.debug(`ezspIncomingMfgTestMessageHandler(): callback called with: [messageType=${messageType}], ` + + `[messageContents=${messageContents.toString('hex')}]`, NS); + } + + /** + * A function used on the Golden Node to switch between normal network operation (for testing) and manufacturing configuration. + * Like emberSleep(), it may not be possible to execute this command due to pending network activity. + * For the transition from normal network operation to manufacturing configuration, it is customary to loop, + * calling this function alternately with emberTick() until the mode change succeeds. + * @param beginConfiguration Determines the new mode of operation. + * true causes the node to enter manufacturing configuration. + * false causes the node to return to normal network operation. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSetPacketMode(beginConfiguration: boolean): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SET_PACKET_MODE); + this.buffalo.writeUInt8(beginConfiguration ? 1 : 0); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * A function used during manufacturing configuration on the Golden Node to send the DUT a reboot command. + * The usual practice is to execute this command at the end of manufacturing configuration, + * to place the DUT into normal network operation for testing. + * This function executes only during manufacturing configuration mode and returns an error otherwise. + * If successful, the DUT acknowledges the reboot command within 20 milliseconds and then reboots. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSendRebootCommand(): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SEND_REBOOT_COMMAND); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * A function used during manufacturing configuration on the Golden Node to set the DUT's 8-byte EUI ID. + * This function executes only during manufacturing configuration mode and returns an error otherwise. + * If successful, the DUT acknowledges the new EUI ID within 150 milliseconds. + * @param newId The 8-byte EUID for the DUT. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSendEui64(newId: EUI64): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SEND_EUI64); + this.buffalo.writeIeeeAddr(newId); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * A function used during manufacturing configuration on the Golden Node to set the DUT's 16-byte configuration string. + * This function executes only during manufacturing configuration mode and will return an error otherwise. + * If successful, the DUT will acknowledge the new string within 150 milliseconds. + * @param newString The 16-byte manufacturing string. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSendManufacturingString(newString: number[]): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SEND_MANUFACTURING_STRING); + this.buffalo.writeListUInt16(newString);// expects 16 bytes + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * A function used during manufacturing configuration on the Golden Node to set the DUT's radio parameters. + * This function executes only during manufacturing configuration mode and returns an error otherwise. + * If successful, the DUT acknowledges the new parameters within 25 milliseconds. + * @param supportedBands Sets the radio band for the DUT. See ember-common.h for possible values. + * @param crystalOffset Sets the CC1020 crystal offset. This parameter has no effect on the EM2420, and it may safely be set to 0 for this RFIC. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSendRadioParameters(supportedBands: number, crystalOffset: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SEND_RADIO_PARAMETERS); + this.buffalo.writeUInt8(supportedBands); + this.buffalo.writeInt8(crystalOffset); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + + /** + * A function used in each of the manufacturing configuration API calls. + * Most implementations will not need to call this function directly. See mfg-test.c for more detail. + * This function executes only during manufacturing configuration mode and returns an error otherwise. + * @param command A pointer to the outgoing command string. + * @returns An sl_status_t value indicating success or failure of the command. + */ + async ezspMfgTestSendCommand(command: number): Promise { + if (this.version < 0x0E) { + throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); + } + + this.startCommand(EzspFrameID.MFG_TEST_SEND_COMMAND); + this.buffalo.writeUInt8(command); + + const sendStatus = await this.sendCommand(); + + if (sendStatus !== EzspStatus.SUCCESS) { + throw new EzspError(sendStatus); + } + + const status = this.buffalo.readUInt32(); + + return status; + } + //----------------------------------------------------------------------------- // ZLL Frames //----------------------------------------------------------------------------- @@ -6953,19 +8430,20 @@ export class Ezsp extends EventEmitter { * @param radioTxPower int8_t Radio transmission power. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspZllNetworkOps(networkInfo: EmberZllNetwork, op: EzspZllNetworkOperation, radioTxPower: number): Promise { + async ezspZllNetworkOps(networkInfo: EmberZllNetwork, op: EzspZllNetworkOperation, radioTxPower: number): Promise { this.startCommand(EzspFrameID.ZLL_NETWORK_OPS); this.buffalo.writeEmberZllNetwork(networkInfo); this.buffalo.writeUInt8(op); this.buffalo.writeInt8(radioTxPower); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6976,18 +8454,19 @@ export class Ezsp extends EventEmitter { * @param securityState EmberZllInitialSecurityState * Initial security state of the network. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspZllSetInitialSecurityState(networkKey: EmberKeyData, securityState: EmberZllInitialSecurityState): Promise { + async ezspZllSetInitialSecurityState(networkKey: EmberKeyData, securityState: EmberZllInitialSecurityState): Promise { this.startCommand(EzspFrameID.ZLL_SET_INITIAL_SECURITY_STATE); this.buffalo.writeEmberKeyData(networkKey); this.buffalo.writeEmberZllInitialSecurityState(securityState); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -6998,17 +8477,18 @@ export class Ezsp extends EventEmitter { * @param securityState EmberZllInitialSecurityState * Security state of the network. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspZllSetSecurityStateWithoutKey(securityState: EmberZllInitialSecurityState): Promise { + async ezspZllSetSecurityStateWithoutKey(securityState: EmberZllInitialSecurityState): Promise { this.startCommand(EzspFrameID.ZLL_SET_SECURITY_STATE_WITHOUT_KEY); this.buffalo.writeEmberZllInitialSecurityState(securityState); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7019,19 +8499,20 @@ export class Ezsp extends EventEmitter { * @param nodeType The node type of the local device. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspZllStartScan(channelMask: number, radioPowerForScan: number, nodeType: EmberNodeType): Promise { + async ezspZllStartScan(channelMask: number, radioPowerForScan: number, nodeType: EmberNodeType): Promise { this.startCommand(EzspFrameID.ZLL_START_SCAN); this.buffalo.writeUInt32(channelMask); this.buffalo.writeInt8(radioPowerForScan); this.buffalo.writeUInt8(nodeType); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7041,17 +8522,18 @@ export class Ezsp extends EventEmitter { * @param durationMs uint32_t The duration in milliseconds to leave the radio on. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspZllSetRxOnWhenIdle(durationMs: number): Promise { + async ezspZllSetRxOnWhenIdle(durationMs: number): Promise { this.startCommand(EzspFrameID.ZLL_SET_RX_ON_WHEN_IDLE); this.buffalo.writeUInt32(durationMs); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7061,13 +8543,12 @@ export class Ezsp extends EventEmitter { * @param networkInfo EmberZllNetwork * Information about the network. * @param isDeviceInfoNull Used to interpret deviceInfo field. * @param deviceInfo EmberZllDeviceInfoRecord * Device specific information. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during reception. + * @param packetInfo Information about the incoming packet received from this network. */ ezspZllNetworkFoundHandler(networkInfo: EmberZllNetwork, isDeviceInfoNull: boolean, deviceInfo: EmberZllDeviceInfoRecord, - lastHopLqi: number, lastHopRssi: number): void { + packetInfo: EmberRxPacketInfo): void { logger.debug(`ezspZllNetworkFoundHandler(): callback called with: [networkInfo=${networkInfo}], [isDeviceInfoNull=${isDeviceInfoNull}], ` - + `[deviceInfo=${deviceInfo}], [lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}]`, NS); + + `[deviceInfo=${deviceInfo}], [packetInfo=${JSON.stringify(packetInfo)}]`, NS); } /** @@ -7075,8 +8556,8 @@ export class Ezsp extends EventEmitter { * This call is fired when a ZLL network scan is complete. * @param status Status of the operation. */ - ezspZllScanCompleteHandler(status: EmberStatus): void { - logger.debug(`ezspZllScanCompleteHandler(): callback called with: [status=${EmberStatus[status]}]`, NS); + ezspZllScanCompleteHandler(status: SLStatus): void { + logger.debug(`ezspZllScanCompleteHandler(): callback called with: [status=${SLStatus[status]}]`, NS); } /** @@ -7084,12 +8565,11 @@ export class Ezsp extends EventEmitter { * This call is fired when network and group addresses are assigned to a remote * mode in a network start or network join request. * @param addressInfo EmberZllAddressAssignment * Address assignment information. - * @param lastHopLqi uint8_t The link quality from the node that last relayed the message. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during reception. + * @param packetInfo Information about the incoming packet received from this network. */ - ezspZllAddressAssignmentHandler(addressInfo: EmberZllAddressAssignment, lastHopLqi: number, lastHopRssi: number): void { - logger.debug(`ezspZllAddressAssignmentHandler(): callback called with: [addressInfo=${addressInfo}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}]`, NS); + ezspZllAddressAssignmentHandler(addressInfo: EmberZllAddressAssignment, packetInfo: EmberRxPacketInfo): void { + logger.debug(`ezspZllAddressAssignmentHandler(): callback called with: [addressInfo=${addressInfo}], ` + + `[packetInfo=${JSON.stringify(packetInfo)}]`, NS); } /** @@ -7109,10 +8589,10 @@ export class Ezsp extends EventEmitter { async ezspZllGetTokens(): Promise<[data: EmberTokTypeStackZllData, security: EmberTokTypeStackZllSecurity]> { this.startCommand(EzspFrameID.ZLL_GET_TOKENS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const data = this.buffalo.readEmberTokTypeStackZllData(); @@ -7129,10 +8609,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.ZLL_SET_DATA_TOKEN); this.buffalo.writeEmberTokTypeStackZllData(data); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7142,10 +8622,10 @@ export class Ezsp extends EventEmitter { async ezspZllSetNonZllNetwork(): Promise { this.startCommand(EzspFrameID.ZLL_SET_NON_ZLL_NETWORK); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7156,13 +8636,13 @@ export class Ezsp extends EventEmitter { async ezspIsZllNetwork(): Promise { this.startCommand(EzspFrameID.IS_ZLL_NETWORK); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const isZllNetwork = this.buffalo.readUInt8() === 1 ? true : false; + const isZllNetwork = this.buffalo.readUInt8() !== 0; return isZllNetwork; } @@ -7175,10 +8655,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.ZLL_SET_RADIO_IDLE_MODE); this.buffalo.writeUInt8(mode); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7189,10 +8669,10 @@ export class Ezsp extends EventEmitter { async ezspZllGetRadioIdleMode(): Promise { this.startCommand(EzspFrameID.ZLL_GET_RADIO_IDLE_MODE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const radioIdleMode = this.buffalo.readUInt8(); @@ -7208,10 +8688,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_ZLL_NODE_TYPE); this.buffalo.writeUInt8(nodeType); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7223,10 +8703,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_ZLL_ADDITIONAL_STATE); this.buffalo.writeUInt16(state); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7237,13 +8717,13 @@ export class Ezsp extends EventEmitter { async ezspZllOperationInProgress(): Promise { this.startCommand(EzspFrameID.ZLL_OPERATION_IN_PROGRESS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const zllOperationInProgress = this.buffalo.readUInt8() === 1 ? true : false; + const zllOperationInProgress = this.buffalo.readUInt8() !== 0; return zllOperationInProgress; } @@ -7254,13 +8734,13 @@ export class Ezsp extends EventEmitter { async ezspZllRxOnWhenIdleGetActive(): Promise { this.startCommand(EzspFrameID.ZLL_RX_ON_WHEN_IDLE_GET_ACTIVE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const zllRxOnWhenIdleGetActive = this.buffalo.readUInt8() === 1 ? true : false; + const zllRxOnWhenIdleGetActive = this.buffalo.readUInt8() !== 0; return zllRxOnWhenIdleGetActive; } @@ -7271,10 +8751,10 @@ export class Ezsp extends EventEmitter { async ezspZllScanningComplete(): Promise { this.startCommand(EzspFrameID.ZLL_SCANNING_COMPLETE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7285,10 +8765,10 @@ export class Ezsp extends EventEmitter { async ezspGetZllPrimaryChannelMask(): Promise { this.startCommand(EzspFrameID.GET_ZLL_PRIMARY_CHANNEL_MASK); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const zllPrimaryChannelMask = this.buffalo.readUInt32(); @@ -7303,10 +8783,10 @@ export class Ezsp extends EventEmitter { async ezspGetZllSecondaryChannelMask(): Promise { this.startCommand(EzspFrameID.GET_ZLL_SECONDARY_CHANNEL_MASK); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const zllSecondaryChannelMask = this.buffalo.readUInt32(); @@ -7323,10 +8803,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_ZLL_PRIMARY_CHANNEL_MASK); this.buffalo.writeUInt32(zllPrimaryChannelMask); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7339,10 +8819,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.SET_ZLL_SECONDARY_CHANNEL_MASK); this.buffalo.writeUInt32(zllSecondaryChannelMask); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7352,118 +8832,11 @@ export class Ezsp extends EventEmitter { async ezspZllClearTokens(): Promise { this.startCommand(EzspFrameID.ZLL_CLEAR_TOKENS); - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - //----------------------------------------------------------------------------- - // WWAH Frames - //----------------------------------------------------------------------------- - - /** - * Sets whether to use parent classification when processing beacons during a - * join or rejoin. Parent classification considers whether a received beacon - * indicates trust center connectivity and long uptime on the network - * @param enabled Enable or disable parent classification - */ - async ezspSetParentClassificationEnabled(enabled: boolean): Promise { - this.startCommand(EzspFrameID.SET_PARENT_CLASSIFICATION_ENABLED); - this.buffalo.writeUInt8(enabled ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Gets whether to use parent classification when processing beacons during a - * join or rejoin. Parent classification considers whether a received beacon - * indicates trust center connectivity and long uptime on the network - * @returns Enable or disable parent classification - */ - async ezspGetParentClassificationEnabled(): Promise { - this.startCommand(EzspFrameID.GET_PARENT_CLASSIFICATION_ENABLED); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const enabled = this.buffalo.readUInt8() === 1 ? true : false; - - return enabled; - } - - /** - * sets the device uptime to be long or short - * @param hasLongUpTime if the uptime is long or not - */ - async ezspSetLongUpTime(hasLongUpTime: boolean): Promise { - this.startCommand(EzspFrameID.SET_LONG_UP_TIME); - this.buffalo.writeUInt8(hasLongUpTime ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * sets the hub connectivity to be true or false - * @param connected if the hub is connected or not - */ - async ezspSetHubConnectivity(connected: boolean): Promise { - this.startCommand(EzspFrameID.SET_HUB_CONNECTIVITY); - this.buffalo.writeUInt8(connected ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * checks if the device uptime is long or short - * @returns if the uptime is long or not - */ - async ezspIsUpTimeLong(): Promise { - this.startCommand(EzspFrameID.IS_UP_TIME_LONG); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const hasLongUpTime = this.buffalo.readUInt8() === 1 ? true : false; - - return hasLongUpTime; - } - - /** - * checks if the hub is connected or not - * @returns if the hub is connected or not - */ - async ezspIsHubConnected(): Promise { - this.startCommand(EzspFrameID.IS_HUB_CONNECTED); - - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const isHubConnected = this.buffalo.readUInt8() === 1 ? true : false; - - return isHubConnected; } //----------------------------------------------------------------------------- @@ -7485,7 +8858,7 @@ export class Ezsp extends EventEmitter { * @returns Whether a GP Pairing has been created or not. */ async ezspGpProxyTableProcessGpPairing(options: number, addr: EmberGpAddress, commMode: number, sinkNetworkAddress: number, - sinkGroupId: number, assignedAlias: number, sinkIeeeAddress: EmberEUI64, gpdKey: EmberKeyData, gpdSecurityFrameCounter: number, + sinkGroupId: number, assignedAlias: number, sinkIeeeAddress: EUI64, gpdKey: EmberKeyData, gpdSecurityFrameCounter: number, forwardingRadius: number): Promise { this.startCommand(EzspFrameID.GP_PROXY_TABLE_PROCESS_GP_PAIRING); this.buffalo.writeUInt32(options); @@ -7499,13 +8872,13 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt32(gpdSecurityFrameCounter); this.buffalo.writeUInt8(forwardingRadius); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const gpPairingAdded = this.buffalo.readUInt8() === 1 ? true : false; + const gpPairingAdded = this.buffalo.readUInt8() !== 0; return gpPairingAdded; } @@ -7522,7 +8895,7 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. */ async ezspDGpSend(action: boolean, useCca: boolean, addr: EmberGpAddress, gpdCommandId: number, gpdAsdu: Buffer, - gpepHandle: number, gpTxQueueEntryLifetimeMs: number): Promise { + gpepHandle: number, gpTxQueueEntryLifetimeMs: number): Promise { this.startCommand(EzspFrameID.D_GP_SEND); this.buffalo.writeUInt8(action ? 1 : 0); this.buffalo.writeUInt8(useCca ? 1 : 0); @@ -7532,13 +8905,14 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(gpepHandle); this.buffalo.writeUInt16(gpTxQueueEntryLifetimeMs); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7549,8 +8923,8 @@ export class Ezsp extends EventEmitter { * @param status An EmberStatus value indicating success or the reason for failure. * @param gpepHandle uint8_t The handle of the GPDF. */ - ezspDGpSentHandler(status: EmberStatus, gpepHandle: number): void { - logger.debug(`ezspDGpSentHandler(): callback called with: [status=${EmberStatus[status]}], [gpepHandle=${gpepHandle}]`, NS); + ezspDGpSentHandler(status: SLStatus, gpepHandle: number): void { + logger.debug(`ezspDGpSentHandler(): callback called with: [status=${SLStatus[status]}], [gpepHandle=${gpepHandle}]`, NS); } /** @@ -7571,10 +8945,10 @@ export class Ezsp extends EventEmitter { * @param proxyTableIndex uint8_tThe proxy table index of the corresponding proxy table entry to the incoming GPDF. * @param gpdCommandPayload uint8_t * The GPD command payload. */ - ezspGpepIncomingMessageHandler(status: EmberStatus, gpdLink: number, sequenceNumber: number, addr: EmberGpAddress, + ezspGpepIncomingMessageHandler(status: EmberGPStatus, gpdLink: number, sequenceNumber: number, addr: EmberGpAddress, gpdfSecurityLevel: EmberGpSecurityLevel, gpdfSecurityKeyType: EmberGpKeyType, autoCommissioning: boolean, bidirectionalInfo: number, gpdSecurityFrameCounter: number, gpdCommandId: number, mic: number, proxyTableIndex: number, gpdCommandPayload: Buffer): void { - logger.debug(`ezspGpepIncomingMessageHandler(): callback called with: [status=${EmberStatus[status]}], [gpdLink=${gpdLink}], ` + logger.debug(`ezspGpepIncomingMessageHandler(): callback called with: [status=${EmberGPStatus[status]}], [gpdLink=${gpdLink}], ` + `[sequenceNumber=${sequenceNumber}], [addr=${JSON.stringify(addr)}], [gpdfSecurityLevel=${EmberGpSecurityLevel[gpdfSecurityLevel]}], ` + `[gpdfSecurityKeyType=${EmberGpKeyType[gpdfSecurityKeyType]}], [autoCommissioning=${autoCommissioning}], ` + `[bidirectionalInfo=${bidirectionalInfo}], [gpdSecurityFrameCounter=${gpdSecurityFrameCounter}], [gpdCommandId=${gpdCommandId}], ` @@ -7616,17 +8990,17 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberGpProxyTableEntry * An EmberGpProxyTableEntry struct containing a copy of the requested proxy entry. */ - async ezspGpProxyTableGetEntry(proxyIndex: number): Promise<[EmberStatus, entry: EmberGpProxyTableEntry]> { + async ezspGpProxyTableGetEntry(proxyIndex: number): Promise<[SLStatus, entry: EmberGpProxyTableEntry]> { this.startCommand(EzspFrameID.GP_PROXY_TABLE_GET_ENTRY); this.buffalo.writeUInt8(proxyIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const entry = this.buffalo.readEmberGpProxyTableEntry(); return [status, entry]; @@ -7641,10 +9015,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GP_PROXY_TABLE_LOOKUP); this.buffalo.writeEmberGpAddress(addr); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const index = this.buffalo.readUInt8(); @@ -7658,17 +9032,17 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the requested sink entry. */ - async ezspGpSinkTableGetEntry(sinkIndex: number): Promise<[EmberStatus, entry: EmberGpSinkTableEntry]> { + async ezspGpSinkTableGetEntry(sinkIndex: number): Promise<[SLStatus, entry: EmberGpSinkTableEntry]> { this.startCommand(EzspFrameID.GP_SINK_TABLE_GET_ENTRY); this.buffalo.writeUInt8(sinkIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const entry = this.buffalo.readEmberGpSinkTableEntry(); return [status, entry]; @@ -7683,10 +9057,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GP_SINK_TABLE_LOOKUP); this.buffalo.writeEmberGpAddress(addr); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const index = this.buffalo.readUInt8(); @@ -7700,18 +9074,19 @@ export class Ezsp extends EventEmitter { * @param entry EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the sink entry to be updated. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspGpSinkTableSetEntry(sinkIndex: number, entry: EmberGpSinkTableEntry): Promise { + async ezspGpSinkTableSetEntry(sinkIndex: number, entry: EmberGpSinkTableEntry): Promise { this.startCommand(EzspFrameID.GP_SINK_TABLE_SET_ENTRY); this.buffalo.writeUInt8(sinkIndex); this.buffalo.writeEmberGpSinkTableEntry(entry); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7723,10 +9098,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GP_SINK_TABLE_REMOVE_ENTRY); this.buffalo.writeUInt8(sinkIndex); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7739,10 +9114,10 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY); this.buffalo.writeEmberGpAddress(addr); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const index = this.buffalo.readUInt8(); @@ -7756,10 +9131,10 @@ export class Ezsp extends EventEmitter { async ezspGpSinkTableClearAll(): Promise { this.startCommand(EzspFrameID.GP_SINK_TABLE_CLEAR_ALL); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7769,10 +9144,10 @@ export class Ezsp extends EventEmitter { async ezspGpSinkTableInit(): Promise { this.startCommand(EzspFrameID.GP_SINK_TABLE_INIT); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7786,10 +9161,10 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(index); this.buffalo.writeUInt32(sfc); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7801,20 +9176,21 @@ export class Ezsp extends EventEmitter { * @param uint8_t sink endpoint. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspGpSinkCommission(options: number, gpmAddrForSecurity: number, gpmAddrForPairing: number, sinkEndpoint: number): Promise { + async ezspGpSinkCommission(options: number, gpmAddrForSecurity: number, gpmAddrForPairing: number, sinkEndpoint: number): Promise { this.startCommand(EzspFrameID.GP_SINK_COMMISSION); this.buffalo.writeUInt8(options); this.buffalo.writeUInt16(gpmAddrForSecurity); this.buffalo.writeUInt16(gpmAddrForPairing); this.buffalo.writeUInt8(sinkEndpoint); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7824,10 +9200,10 @@ export class Ezsp extends EventEmitter { async ezspGpTranslationTableClear(): Promise { this.startCommand(EzspFrameID.GP_TRANSLATION_TABLE_CLEAR); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7838,15 +9214,15 @@ export class Ezsp extends EventEmitter { async ezspGpSinkTableGetNumberOfActiveEntries(): Promise { this.startCommand(EzspFrameID.GP_SINK_TABLE_GET_NUMBER_OF_ACTIVE_ENTRIES); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const number_of_entries = this.buffalo.readUInt8(); + const numberOfEntries = this.buffalo.readUInt8(); - return number_of_entries; + return numberOfEntries; } //----------------------------------------------------------------------------- @@ -7860,10 +9236,10 @@ export class Ezsp extends EventEmitter { async ezspGetTokenCount(): Promise { this.startCommand(EzspFrameID.GET_TOKEN_COUNT); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } const count = this.buffalo.readUInt8(); @@ -7877,17 +9253,17 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberTokenInfo * Token information. */ - async ezspGetTokenInfo(index: number): Promise<[EmberStatus, tokenInfo: EmberTokenInfo]> { + async ezspGetTokenInfo(index: number): Promise<[SLStatus, tokenInfo: EmberTokenInfo]> { this.startCommand(EzspFrameID.GET_TOKEN_INFO); this.buffalo.writeUInt8(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const tokenInfo = this.buffalo.readEmberTokenInfo(); return [status, tokenInfo]; @@ -7900,18 +9276,18 @@ export class Ezsp extends EventEmitter { * @returns An EmberStatus value indicating success or the reason for failure. * @returns EmberTokenData * Token Data */ - async ezspGetTokenData(token: number, index: number): Promise<[EmberStatus, tokenData: EmberTokenData]> { + async ezspGetTokenData(token: number, index: number): Promise<[SLStatus, tokenData: EmberTokenData]> { this.startCommand(EzspFrameID.GET_TOKEN_DATA); this.buffalo.writeUInt32(token); this.buffalo.writeUInt32(index); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); const tokenData = this.buffalo.readEmberTokenData(); return [status, tokenData]; @@ -7924,19 +9300,19 @@ export class Ezsp extends EventEmitter { * @param EmberTokenData * Token Data * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspSetTokenData(token: number, index: number, tokenData: EmberTokenData): Promise { + async ezspSetTokenData(token: number, index: number, tokenData: EmberTokenData): Promise { this.startCommand(EzspFrameID.SET_TOKEN_DATA); this.buffalo.writeUInt32(token); this.buffalo.writeUInt32(index); this.buffalo.writeEmberTokenData(tokenData); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - const status: EmberStatus = this.buffalo.readUInt8(); + const status = this.buffalo.readStatus(this.version); return status; } @@ -7946,10 +9322,10 @@ export class Ezsp extends EventEmitter { async ezspResetNode(): Promise { this.startCommand(EzspFrameID.RESET_NODE); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } @@ -7957,16 +9333,17 @@ export class Ezsp extends EventEmitter { * Run GP security test vectors. * @returns An EmberStatus value indicating success or the reason for failure. */ - async ezspGpSecurityTestVectors(): Promise { + async ezspGpSecurityTestVectors(): Promise { this.startCommand(EzspFrameID.GP_SECURITY_TEST_VECTORS); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } - - const status: EmberStatus = this.buffalo.readUInt8(); + + const status = this.buffalo.readStatus(this.version); + return status; } @@ -7980,10 +9357,10 @@ export class Ezsp extends EventEmitter { this.buffalo.writeUInt8(excludeOutgoingFC ? 1 : 0); this.buffalo.writeUInt8(excludeBootCounter ? 1 : 0); - const sendStatus: EzspStatus = await this.sendCommand(); + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); + throw new EzspError(sendStatus); } } } diff --git a/src/adapter/ember/ezspError.ts b/src/adapter/ember/ezspError.ts new file mode 100644 index 0000000000..f25b67c7ed --- /dev/null +++ b/src/adapter/ember/ezspError.ts @@ -0,0 +1,10 @@ +import {EzspStatus} from "./enums"; + +export class EzspError extends Error { + public code: EzspStatus; + + constructor (code: EzspStatus) { + super(EzspStatus[code]); + this.code = code; + } +} diff --git a/src/adapter/ember/types.ts b/src/adapter/ember/types.ts index 95503152e1..c40c07120e 100644 --- a/src/adapter/ember/types.ts +++ b/src/adapter/ember/types.ts @@ -1,3 +1,4 @@ +import {EUI64, ExtendedPanId, NodeId, PanId} from '../../zspec/tstypes'; import { EmberApsOption, EmberBindingType, @@ -16,20 +17,8 @@ import { SecManDerivedKeyType, SecManFlag, SecManKeyType -} from "./enums"; +} from './enums'; -/** - * EUI 64-bit ID (IEEE 802.15.4 long address). uint8_t[EUI64_SIZE] - * - * EXPECTED WITH 0x PREFIX - */ -export type EmberEUI64 = string; -/** IEEE 802.15.4 node ID. Also known as short address. uint16_t */ -export type EmberNodeId = number; -/** IEEE 802.15.4 PAN ID. uint16_t */ -export type EmberPanId = number; -/** PAN 64-bit ID (IEEE 802.15.4 long address). uint8_t[EXTENDED_PAN_ID_SIZE] */ -export type EmberExtendedPanId = number[]; /** 16-bit ZigBee multicast group identifier. uint16_t */ export type EmberMulticastId = number; /** @@ -107,9 +96,9 @@ export type EmberNetworkInitStruct = { */ export type EmberNetworkParameters = { /** The network's extended PAN identifier. int8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, + extendedPanId: ExtendedPanId, /** The network's PAN identifier. uint16_t*/ - panId: EmberPanId, + panId: PanId, /** A power setting, in dBm. int8_t*/ radioTxPower: number, /** A radio channel. Be sure to specify a channel supported by the radio. uint8_t */ @@ -123,7 +112,7 @@ export type EmberNetworkParameters = { * NWK Manager ID. The ID of the network manager in the current network. * This may only be set at joining when using EMBER_USE_CONFIGURED_NWK_STATE as the join method. */ - nwkManagerId: EmberNodeId, + nwkManagerId: NodeId, /** * An NWK Update ID. The value of the ZigBee nwkUpdateId known by the stack. * It is used to determine the newest instance of the network after a PAN @@ -143,8 +132,8 @@ export type EmberNetworkParameters = { /** Defines a beacon entry that is processed when scanning, joining, or rejoining. */ export type EmberBeaconData = { - panId: EmberPanId, - sender: EmberNodeId, + panId: PanId, + sender: NodeId, /** uint8_t */ channel: number, /** uint8_t */ @@ -179,7 +168,7 @@ export type EmberBeaconData = { /** default true */, endDeviceKeepalive : boolean, /** uint8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, + extendedPanId: ExtendedPanId, }; /** @@ -199,11 +188,11 @@ export type EmberMultiPhyRadioParameters = { /** This structure contains information about child nodes. */ export type EmberChildData = { /** */ - eui64: EmberEUI64, + eui64: EUI64, /** */ type: EmberNodeType, /** */ - id: EmberNodeId, + id: NodeId, /** uint8_t */ phy: number, /** uint8_t */ @@ -250,7 +239,7 @@ export type EmberNeighborTableEntry = { * */ age: number, /** The 8 byte EUI64 of the neighbor. */ - longId: EmberEUI64, + longId: EUI64, }; /** @@ -297,7 +286,7 @@ export type EmberDutyCycleLimits = { /** A structure containing, per device, overall duty cycle consumed (up to the suspend limit). */ export type EmberPerDeviceDutyCycle = { /** Node ID of the device whose duty cycle is reported. */ - nodeId: EmberNodeId, + nodeId: NodeId, /** The amount of overall duty cycle consumed (up to suspend limit). */ dutyCycleConsumed: EmberDutyCycleHectoPct, }; @@ -337,7 +326,7 @@ export type EmberBindingTableEntry = { * - The destination EUI64, for unicasts. * - A 16-bit multicast group address, for multicasts. */ - identifier: EmberEUI64, + identifier: EUI64, /** The index of the network the binding belongs to. uint8_t */ networkIndex: number, }; @@ -428,7 +417,7 @@ export type EmberInitialSecurityState = { * This field must be set when using commissioning mode. * It is required to be in little-endian format. */ - preconfiguredTrustCenterEui64: EmberEUI64, + preconfiguredTrustCenterEui64: EUI64, }; /** This describes the security features used by the stack for a joined device. */ @@ -439,7 +428,7 @@ export type EmberCurrentSecurityState = { * This indicates the EUI64 of the Trust Center. * It will be all zeroes if the Trust Center Address is not known (i.e., the device is in a Distributed Trust Center network). */ - trustCenterLongAddress: EmberEUI64, + trustCenterLongAddress: EUI64, }; /** @@ -452,7 +441,7 @@ export type SecManContext = { /** uint8_t */ keyIndex: number, derivedType: SecManDerivedKeyType, - eui64: EmberEUI64, + eui64: EUI64, /** uint8_t */ multiNetworkIndex: number, flags: SecManFlag, @@ -566,13 +555,13 @@ export type EmberPrivateKeyData = { /** Defines a ZigBee network and the associated parameters. */ export type EmberZigbeeNetwork = { /** uint16_t */ - panId: EmberPanId, + panId: PanId, /** uint8_t */ channel: number, /** bool */ allowingJoin: number, /** uint8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, + extendedPanId: ExtendedPanId, /** uint8_t */ stackProfile: number, /** uint8_t */ @@ -593,8 +582,8 @@ export type EmberZllSecurityAlgorithmData = { export type EmberZllNetwork = { zigbeeNetwork: EmberZigbeeNetwork, securityAlgorithm: EmberZllSecurityAlgorithmData, - eui64: EmberEUI64, - nodeId: EmberNodeId, + eui64: EUI64, + nodeId: NodeId, state: EmberZllState, nodeType: EmberNodeType, /** uint8_t */ @@ -619,7 +608,7 @@ export type EmberZllInitialSecurityState = { /** Information discovered during a ZLL scan about the ZLL device's endpoint information. */ export type EmberZllDeviceInfoRecord = { - ieeeAddress: EmberEUI64, + ieeeAddress: EUI64, /** uint8_t */ endpointId: number, /** uint16_t */ @@ -634,9 +623,9 @@ export type EmberZllDeviceInfoRecord = { /** Network and group address assignment information. */ export type EmberZllAddressAssignment = { - nodeId: EmberNodeId, - freeNodeIdMin: EmberNodeId, - freeNodeIdMax: EmberNodeId, + nodeId: NodeId, + freeNodeIdMin: NodeId, + freeNodeIdMax: NodeId, groupIdMin: EmberMulticastId, groupIdMax: EmberMulticastId, freeGroupIdMin: EmberMulticastId, @@ -681,7 +670,7 @@ export type EmberGpSourceId = number; export type EmberGpAddress = { // union { /** The IEEE address is used when the application identifier is ::EMBER_GP_APPLICATION_IEEE_ADDRESS. */ - gpdIeeeAddress?: EmberEUI64, + gpdIeeeAddress?: EUI64, /** The 32-bit source identifier is used when the application identifier is ::EMBER_GP_APPLICATION_SOURCE_ID. */ sourceId?: EmberGpSourceId, // } id; @@ -703,7 +692,7 @@ export type EmberGpProxyTableEntry = { /** The addressing info of the GPD */ gpd: EmberGpAddress, /** The assigned alias for the GPD */ - assignedAlias: EmberNodeId, + assignedAlias: NodeId, /** The security options field. uint8_t */ securityOptions: number, /** The SFC of the GPD */ @@ -721,9 +710,9 @@ export type EmberGpProxyTableEntry = { /** GP Sink Address. */ export type EmberGpSinkAddress = { /** EUI64 or long address of the sink */ - sinkEUI: EmberEUI64, + sinkEUI: EUI64, /** Node ID or network address of the sink */ - sinkNodeId: EmberNodeId, + sinkNodeId: NodeId, }; /** GP Sink Group. */ @@ -760,7 +749,7 @@ export type EmberGpSinkTableEntry = { /** The list of sinks; hardcoded to 2, which is the spec minimum. EmberGpSinkListEntry[GP_SINK_LIST_ENTRIES] */ sinkList: EmberGpSinkListEntry[], /** The assigned alias for the GPD */ - assignedAlias: EmberNodeId, + assignedAlias: NodeId, /** The groupcast radius. uint8_t */ groupcastRadius: number, /** The security options field. uint8_t */ @@ -795,7 +784,7 @@ export type EmberTokenData = { /** This data structure contains the transient key data that is used during Zigbee 3.0 joining. */ export type EmberTransientKeyData = { - eui64: EmberEUI64, + eui64: EUI64, /** uint32_t */ incomingFrameCounter: number, bitmask: EmberKeyStructBitmask, @@ -810,3 +799,66 @@ export type EmberTransientKeyData = { psa_id?: number, // }, }; + +/** + * Endpoint information (a ZigBee Simple Descriptor). + * + * This is a ZigBee Simple Descriptor and contains information about an endpoint. + * This information is shared with other nodes in the network by the ZDO. + */ +export type EmberEndpointDescription = { + /** Identifies the endpoint's application profile. uint16_t */ + profileId: number; + /** The endpoint's device ID within the application profile. uint16_t */ + deviceId: number; + /** The endpoint's device version. uint8_t */ + deviceVersion: number; + /** The number of input clusters. uint8_t */ + inputClusterCount: number; + /** The number of output clusters. uint8_t */ + outputClusterCount: number; +}; + +export type EmberMultiprotocolPriorities = { + /** The priority of a Zigbee RX operation while not receiving a packet. uint8_t */ + backgroundRx: number; + /** The priority of a Zigbee TX operation. uint8_t */ + tx: number; + /** The priority of a Zigbee RX operation while receiving a packet. uint8_t */ + activeRx: number; +}; + + +/** @brief Received packet information. + * + * Contains information about the incoming packet. + */ +export type EmberRxPacketInfo = { + /** Short ID of the sender of the message */ + senderShortId: NodeId; + /** + * EUI64 of the sender of the message if the sender chose to include this information in the message. + * The ::SL_ZIGBEE_APS_OPTION_SOURCE_EUI64 bit in the options field of the APS frame of the incoming message indicates that + * the EUI64 is present in the message. + * Also, when not set, the sender long ID is set to all zeros + */ + senderLongId: EUI64; + /** + * The index of the entry in the binding table that matches the sender of the message or 0xFF if there is no matching entry. + * A binding matches the message if: + * - The binding's source endpoint is the same as the message's destination endpoint + * - The binding's destination endpoint is the same as the message's source endpoint + * - The source of the message has been previously identified as the binding's remote node by a successful address discovery + * or by the application via a call to either ::sl_zigbee_set_reply_binding() or ::sl_zigbee_note_senders_binding(). + * uint8_t + */ + bindingIndex: number; + /** The index of the entry in the address table that matches the sender of the message or 0xFF if there is no matching entry. uint8_t */ + addressIndex: number; + /** Link quality of the node that last relayed the current message. uint8_t */ + lastHopLqi: number; + /** Received signal strength indicator (RSSI) of the node that last relayed the message. int8_t */ + lastHopRssi: number; + /* Timestamp of the moment when Start Frame Delimiter (SFD) was received. uint32_t */ + lastHopTimestamp: number; +}; diff --git a/src/adapter/ember/zdo.ts b/src/adapter/ember/zdo.ts index a30f240a5d..e4531620a5 100644 --- a/src/adapter/ember/zdo.ts +++ b/src/adapter/ember/zdo.ts @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // ZigBee Device Object (ZDO) -import {EmberEUI64, EmberExtendedPanId, EmberNodeId} from "./types"; +import {EUI64, ExtendedPanId, NodeId} from "../../zspec/tstypes"; /** The endpoint where the ZigBee Device Object (ZDO) resides. */ export const ZDO_ENDPOINT = 0; @@ -97,16 +97,16 @@ export type ZDOLQITableEntry = { * * 64-bit */ - extendedPanId: EmberExtendedPanId, + extendedPanId: ExtendedPanId, /** * 64-bit IEEE address that is unique to every device. * If this value is unknown at the time of the request, this field shall be set to 0xffffffffffffffff. * * 64-bit */ - eui64: EmberEUI64, + eui64: EUI64, /** The 16-bit network address of the neighboring device. 16-bit */ - nodeId: EmberNodeId, + nodeId: NodeId, /** * The type of the neighbor device: * 0x00 = ZigBee coordinator @@ -168,7 +168,7 @@ export type ZDOLQITableEntry = { export type ZDORoutingTableEntry = { /** 16-bit network address of this route */ - destinationAddress: EmberNodeId, + destinationAddress: NodeId, /** * Status of the route * 0x0=ACTIVE. @@ -202,12 +202,12 @@ export type ZDORoutingTableEntry = { /** 2-bit */ reserved: number, /** 16-bit network address of the next hop on the way to the destination. */ - nextHopAddress: EmberNodeId, + nextHopAddress: NodeId, }; export type ZDOBindingTableEntry = { /** The source IEEE address for the binding entry. */ - sourceEui64: EmberEUI64, + sourceEui64: EUI64, /** The source endpoint for the binding entry. uint8_t */ sourceEndpoint: number, /** The identifier of the cluster on the source device that is bound to the destination device. uint16_t */ @@ -224,7 +224,7 @@ export type ZDOBindingTableEntry = { */ destAddrMode: number, /** The destination address for the binding entry. uint16_t or uint8_t[EUI64_SIZE] */ - dest: EmberNodeId | EmberEUI64, + dest: NodeId | EUI64, /** * This field shall be present only if the DstAddrMode field has a value of 0x03 and, if present, * shall be the destination endpoint for the binding entry. @@ -235,27 +235,27 @@ export type ZDOBindingTableEntry = { /** @see IEEE_ADDRESS_RESPONSE */ export type IEEEAddressResponsePayload = { - eui64: EmberEUI64, - nodeId: EmberNodeId, + eui64: EUI64, + nodeId: NodeId, assocDevList: number[], }; /** @see NETWORK_ADDRESS_RESPONSE */ export type NetworkAddressResponsePayload = { - eui64: EmberEUI64, - nodeId: EmberNodeId, + eui64: EUI64, + nodeId: NodeId, assocDevList: number[], }; /** @see MATCH_DESCRIPTORS_RESPONSE */ export type MatchDescriptorsResponsePayload = { - nodeId: EmberNodeId, + nodeId: NodeId, endpointList: number[], }; /** @see SIMPLE_DESCRIPTOR_RESPONSE */ export type SimpleDescriptorResponsePayload = { - nodeId: EmberNodeId, + nodeId: NodeId, /** uint8_t */ // inClusterCount: number, /** const uint16_t* */ @@ -274,7 +274,7 @@ export type SimpleDescriptorResponsePayload = { /** @see NODE_DESCRIPTOR_RESPONSE */ export type NodeDescriptorResponsePayload = { - nodeId: EmberNodeId, + nodeId: NodeId, logicalType: number, macCapFlags: MACCapabilityFlags, manufacturerCode: number, @@ -283,7 +283,7 @@ export type NodeDescriptorResponsePayload = { /** @see POWER_DESCRIPTOR_RESPONSE */ export type PowerDescriptorResponsePayload = { - nodeId: EmberNodeId, + nodeId: NodeId, currentPowerMode: number, availPowerSources: number, currentPowerSource: number, @@ -292,7 +292,7 @@ export type PowerDescriptorResponsePayload = { /** @see ACTIVE_ENDPOINTS_RESPONSE */ export type ActiveEndpointsResponsePayload = { - nodeId: EmberNodeId, + nodeId: NodeId, endpointList: number[], }; @@ -316,14 +316,14 @@ export type BindingTableResponsePayload = { /** @see END_DEVICE_ANNOUNCE */ export type EndDeviceAnnouncePayload = { - nodeId: EmberNodeId, - eui64: EmberEUI64, + nodeId: NodeId, + eui64: EUI64, capabilities: MACCapabilityFlags, }; /** @see PARENT_ANNOUNCE_RESPONSE */ export type ParentAnnounceResponsePayload = { - children: EmberEUI64[], + children: EUI64[], }; /** diff --git a/test/adapter/ember/requestQueue.test.ts b/test/adapter/ember/requestQueue.test.ts index 9a036c9bc5..7e2ceb7759 100644 --- a/test/adapter/ember/requestQueue.test.ts +++ b/test/adapter/ember/requestQueue.test.ts @@ -1,24 +1,25 @@ -import {EmberRequestQueue, NETWORK_BUSY_DEFER_MSEC, NETWORK_DOWN_DEFER_MSEC} from '../../../src/adapter/ember/adapter/requestQueue'; -import {EmberStatus, EzspStatus} from '../../../src/adapter/ember/enums'; +import {EmberRequestQueue, BUSY_DEFER_MSEC, NETWORK_DOWN_DEFER_MSEC} from '../../../src/adapter/ember/adapter/requestQueue'; +import {SLStatus, EzspStatus} from '../../../src/adapter/ember/enums'; +import {EzspError} from '../../../src/adapter/ember/ezspError'; import {Wait} from '../../../src/utils'; let fakeWaitTime = 1000; -let varyingReturn: EmberStatus = EmberStatus.SUCCESS; -const getVaryingReturn = async (): Promise => { +let varyingReturn: SLStatus = SLStatus.OK; +const getVaryingReturn = async (): Promise => { await Wait(fakeWaitTime); return varyingReturn; }; -const getThrownError = async (): Promise => { +const getThrownError = async (): Promise => { await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.ASH_ACK_TIMEOUT]); + throw new EzspError(EzspStatus.ASH_ACK_TIMEOUT); } -const getThrowNetworkBusy = async (): Promise => { +const getThrowNetworkBusy = async (): Promise => { await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.NO_TX_SPACE]); + throw new EzspError(EzspStatus.NO_TX_SPACE); }; -const getThrowNetworkDown = async (): Promise => { +const getThrowNetworkDown = async (): Promise => { await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.NOT_CONNECTED]); + throw new EzspError(EzspStatus.NOT_CONNECTED); }; class TestThis { @@ -33,13 +34,13 @@ class TestThis { public async getNewBS(): Promise { await new Promise((resolve, reject): void => { this.q.enqueue( - async (): Promise => { + async (): Promise => { await Wait(fakeWaitTime); this.bs = true; resolve(); - return EmberStatus.SUCCESS; + return SLStatus.OK; }, reject, ) @@ -70,20 +71,20 @@ describe('Ember Request Queue', () => { afterEach(() => { fakeWaitTime = 1000; - varyingReturn = EmberStatus.SUCCESS; + varyingReturn = SLStatus.OK; requestQueue.stopDispatching(); }); it('Queues request and resolves it', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.SUCCESS; + varyingReturn = SLStatus.OK; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -118,13 +119,13 @@ describe('Ember Request Queue', () => { it('Queues request, rejects it on error, and removes it from queue', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.ERR_FATAL; + varyingReturn = SLStatus.FAIL; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -148,7 +149,7 @@ describe('Ember Request Queue', () => { jest.advanceTimersByTime(fakeWaitTime + 20); - await expect(p).rejects.toStrictEqual(new Error(EmberStatus[varyingReturn])); + await expect(p).rejects.toStrictEqual(new Error(SLStatus[varyingReturn])); expect(funcSpy).toHaveBeenCalledTimes(1); expect(funcRejectSpy).toHaveBeenCalledTimes(1); @@ -161,10 +162,10 @@ describe('Ember Request Queue', () => { const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getThrownError(); + async (): Promise => { + const status: SLStatus = await getThrownError(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -187,7 +188,7 @@ describe('Ember Request Queue', () => { requestQueue.startDispatching(); jest.advanceTimersByTime(fakeWaitTime + 20); - await expect(p).rejects.toStrictEqual(new Error(EzspStatus[EzspStatus.ASH_ACK_TIMEOUT])); + await expect(p).rejects.toStrictEqual(new EzspError(EzspStatus.ASH_ACK_TIMEOUT)); expect(funcSpy).toHaveBeenCalledTimes(1); expect(funcRejectSpy).toHaveBeenCalledTimes(1); @@ -195,16 +196,16 @@ describe('Ember Request Queue', () => { expect(requestQueue.queue).toHaveLength(0);// no longer in queue }); - it('Queues request, defers on NETWORK_BUSY and defers again on NETWORK_DOWN', async () => { + it('Queues request, defers on BUSY and defers again on NETWORK_DOWN', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.NETWORK_BUSY; + varyingReturn = SLStatus.BUSY; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -225,16 +226,16 @@ describe('Ember Request Queue', () => { requestQueue.startDispatching(); - await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (NETWORK_BUSY_DEFER_MSEC * 0.25)); + await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (BUSY_DEFER_MSEC * 0.25)); expect(deferSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private expect(requestQueue.queue).toHaveLength(1);// still in queue - await jest.advanceTimersByTimeAsync(NETWORK_BUSY_DEFER_MSEC + 20); + await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); - varyingReturn = EmberStatus.NETWORK_DOWN; + varyingReturn = SLStatus.NETWORK_DOWN; await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (NETWORK_DOWN_DEFER_MSEC * 0.25)); @@ -246,16 +247,16 @@ describe('Ember Request Queue', () => { await jest.advanceTimersByTimeAsync(NETWORK_DOWN_DEFER_MSEC + 20); }); - it('Queues request, defers on NETWORK_BUSY and then resolves it', async () => { + it('Queues request, defers on BUSY and then resolves it', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.NETWORK_BUSY; + varyingReturn = SLStatus.BUSY; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -275,16 +276,16 @@ describe('Ember Request Queue', () => { requestQueue.startDispatching(); - await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (NETWORK_BUSY_DEFER_MSEC * 0.25)); + await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (BUSY_DEFER_MSEC * 0.25)); expect(deferSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private expect(requestQueue.queue).toHaveLength(1);// still in queue - await jest.advanceTimersByTimeAsync(NETWORK_BUSY_DEFER_MSEC + 20); + await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); - varyingReturn = EmberStatus.SUCCESS; + varyingReturn = SLStatus.OK; await jest.advanceTimersByTimeAsync(fakeWaitTime + 20); @@ -295,18 +296,18 @@ describe('Ember Request Queue', () => { expect(requestQueue.queue).toHaveLength(0);// no longer in queue }); - it('Queues request, defers on NETWORK_BUSY and only retries once after internal change', async () => { + it('Queues request, defers on BUSY and only retries once after internal change', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.NETWORK_BUSY; + varyingReturn = SLStatus.BUSY; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { // internally changes external parameter that changes the queue's next run - varyingReturn = EmberStatus.SUCCESS; + varyingReturn = SLStatus.OK; return status; } @@ -326,14 +327,14 @@ describe('Ember Request Queue', () => { requestQueue.startDispatching(); - await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (NETWORK_BUSY_DEFER_MSEC * 0.25)); + await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (BUSY_DEFER_MSEC * 0.25)); expect(deferSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private expect(requestQueue.queue).toHaveLength(1);// still in queue - await jest.advanceTimersByTimeAsync(NETWORK_BUSY_DEFER_MSEC + 20); + await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); await jest.advanceTimersByTimeAsync(fakeWaitTime + 20); @@ -344,15 +345,15 @@ describe('Ember Request Queue', () => { expect(requestQueue.queue).toHaveLength(0);// no longer in queue }); - it('Queues request, defers on thrown NETWORK_BUSY', async () => { + it('Queues request, defers on thrown BUSY', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getThrowNetworkBusy(); + async (): Promise => { + const status: SLStatus = await getThrowNetworkBusy(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -372,26 +373,26 @@ describe('Ember Request Queue', () => { requestQueue.startDispatching(); - await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (NETWORK_BUSY_DEFER_MSEC * 0.25)); + await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (BUSY_DEFER_MSEC * 0.25)); expect(deferSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private expect(requestQueue.queue).toHaveLength(1);// still in queue - await jest.advanceTimersByTimeAsync(NETWORK_BUSY_DEFER_MSEC + 20); + await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); }); it('Queues request and resolves by priority', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = EmberStatus.SUCCESS; + varyingReturn = SLStatus.OK; const p = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } @@ -403,10 +404,10 @@ describe('Ember Request Queue', () => { }); const pPrio = new Promise((resolve, reject) => { requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); + async (): Promise => { + const status: SLStatus = await getVaryingReturn(); - if (status !== EmberStatus.SUCCESS) { + if (status !== SLStatus.OK) { return status; } From 49ab324b2de721292d1a6e4847ee6c0746074054 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 15:54:11 +0200 Subject: [PATCH 03/17] Remove EmberStatus enum. --- src/adapter/ember/enums.ts | 446 ------------------------------------- 1 file changed, 446 deletions(-) diff --git a/src/adapter/ember/enums.ts b/src/adapter/ember/enums.ts index 76a51d106c..799ee95add 100644 --- a/src/adapter/ember/enums.ts +++ b/src/adapter/ember/enums.ts @@ -752,452 +752,6 @@ export enum SLStatus { ZIGBEE_EZSP_ERROR = 0x0C1E, }; -// /** -// * Many EmberZNet API functions return an ::EmberStatus value to indicate the success or failure of the call. -// * Return codes are one byte long. -// */ -// export enum EmberStatus { -// // Generic Messages. These messages are system wide. -// /** The generic "no error" message. */ -// SUCCESS = 0x00, -// /** The generic "fatal error" message. */ -// ERR_FATAL = 0x01, -// /** An invalid value was passed as an argument to a function. */ -// BAD_ARGUMENT = 0x02, -// /** The requested information was not found. */ -// NOT_FOUND = 0x03, -// /** The manufacturing and stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ -// EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04, -// /** The manufacturing token format in non-volatile memory is different than what the stack expects (returned at initialization). */ -// EEPROM_MFG_VERSION_MISMATCH = 0x06, -// /** The stack token format in non-volatile memory is different than what the stack expects (returned at initialization). */ -// EEPROM_STACK_VERSION_MISMATCH = 0x07, - -// // Packet Buffer Module Errors -// /** There are no more buffers. */ -// NO_BUFFERS = 0x18, -// /** Packet is dropped by packet-handoff callbacks. */ -// PACKET_HANDOFF_DROP_PACKET = 0x19, - -// // Serial Manager Errors -// /** Specifies an invalid baud rate. */ -// SERIAL_INVALID_BAUD_RATE = 0x20, -// /** Specifies an invalid serial port. */ -// SERIAL_INVALID_PORT = 0x21, -// /** Tried to send too much data. */ -// SERIAL_TX_OVERFLOW = 0x22, -// /** There wasn't enough space to store a received character and the character was dropped. */ -// SERIAL_RX_OVERFLOW = 0x23, -// /** Detected a UART framing error. */ -// SERIAL_RX_FRAME_ERROR = 0x24, -// /** Detected a UART parity error. */ -// SERIAL_RX_PARITY_ERROR = 0x25, -// /** There is no received data to process. */ -// SERIAL_RX_EMPTY = 0x26, -// /** The receive interrupt was not handled in time and a character was dropped. */ -// SERIAL_RX_OVERRUN_ERROR = 0x27, - -// // MAC Errors -// /** The MAC transmit queue is full. */ -// MAC_TRANSMIT_QUEUE_FULL = 0x39, -// // Internal -// /** MAC header FCF error on receive. */ -// MAC_UNKNOWN_HEADER_TYPE = 0x3A, -// /** MAC ACK header received. */ -// MAC_ACK_HEADER_TYPE = 0x3B, -// /** The MAC can't complete this task because it is scanning. */ -// MAC_SCANNING = 0x3D, -// /** No pending data exists for a data poll. */ -// MAC_NO_DATA = 0x31, -// /** Attempts to scan when joined to a network. */ -// MAC_JOINED_NETWORK = 0x32, -// /** Scan duration must be 0 to 14 inclusive. Tried to scan with an incorrect duration value. */ -// MAC_BAD_SCAN_DURATION = 0x33, -// /** emberStartScan was called with an incorrect scan type. */ -// MAC_INCORRECT_SCAN_TYPE = 0x34, -// /** emberStartScan was called with an invalid channel mask. */ -// MAC_INVALID_CHANNEL_MASK = 0x35, -// /** Failed to scan the current channel because the relevant MAC command could not be transmitted. */ -// MAC_COMMAND_TRANSMIT_FAILURE = 0x36, -// /** An ACK was expected following the transmission but the MAC level ACK was never received. */ -// MAC_NO_ACK_RECEIVED = 0x40, -// /** MAC failed to transmit a message because it could not successfully perform a radio network switch. */ -// MAC_RADIO_NETWORK_SWITCH_FAILED = 0x41, -// /** An indirect data message timed out before a poll requested it. */ -// MAC_INDIRECT_TIMEOUT = 0x42, - -// // Simulated EEPROM Errors -// /** -// * The Simulated EEPROM is telling the application that at least one flash page to be erased. -// * The GREEN status means the current page has not filled above the ::ERASE_CRITICAL_THRESHOLD. -// * -// * The application should call the function ::halSimEepromErasePage() when it can to erase a page. -// */ -// SIM_EEPROM_ERASE_PAGE_GREEN = 0x43, -// /** -// * The Simulated EEPROM is telling the application that at least one flash page must be erased. -// * The RED status means the current page has filled above the ::ERASE_CRITICAL_THRESHOLD. -// * -// * Due to the shrinking availability of write space, data could be lost. -// * The application must call the function ::halSimEepromErasePage() as soon as possible to erase a page. -// */ -// SIM_EEPROM_ERASE_PAGE_RED = 0x44, -// /** -// * The Simulated EEPROM has run out of room to write new data and the data trying to be set has been lost. -// * This error code is the result of ignoring the ::SIM_EEPROM_ERASE_PAGE_RED error code. -// * -// * The application must call the function ::halSimEepromErasePage() to make room for any further calls to set a token. -// */ -// SIM_EEPROM_FULL = 0x45, -// // Errors 46 and 47 are now defined below in the flash error block (was attempting to prevent renumbering). -// /** -// * Attempt 1 to initialize the Simulated EEPROM has failed. -// * -// * This failure means the information already stored in the Flash (or a lack thereof), -// * is fatally incompatible with the token information compiled into the code image being run. -// */ -// SIM_EEPROM_INIT_1_FAILED = 0x48, -// /** -// * Attempt 2 to initialize the Simulated EEPROM has failed. -// * -// * This failure means Attempt 1 failed, and the token system failed to properly reload default tokens and reset the Simulated EEPROM. -// */ -// SIM_EEPROM_INIT_2_FAILED = 0x49, -// /** -// * Attempt 3 to initialize the Simulated EEPROM has failed. -// * -// * This failure means one or both of the tokens ::TOKEN_MFG_NVDATA_VERSION or ::TOKEN_STACK_NVDATA_VERSION -// * were incorrect and the token system failed to properly reload default tokens and reset the Simulated EEPROM. -// */ -// SIM_EEPROM_INIT_3_FAILED = 0x4A, -// /** -// * The Simulated EEPROM is repairing itself. -// * -// * While there's nothing for an app to do when the SimEE is going to -// * repair itself (SimEE has to be fully functional for the rest of the -// * system to work), alert the application to the fact that repair -// * is occurring. There are debugging scenarios where an app might want -// * to know that repair is happening, such as monitoring frequency. -// * @note Common situations will trigger an expected repair, such as -// * using an erased chip or changing token definitions. -// */ -// SIM_EEPROM_REPAIRING = 0x4D, - -// // Flash Errors -// /** -// * A fatal error has occurred while trying to write data to the Flash. -// * The target memory attempting to be programmed is already programmed. -// * The flash write routines were asked to flip a bit from a 0 to 1, -// * which is physically impossible and the write was therefore inhibited. -// * The data in the Flash cannot be trusted after this error. -// */ -// ERR_FLASH_WRITE_INHIBITED = 0x46, -// /** -// * A fatal error has occurred while trying to write data to the Flash and the write verification has failed. -// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. -// */ -// ERR_FLASH_VERIFY_FAILED = 0x47, -// /** -// * A fatal error has occurred while trying to write data to the Flash possibly due to write protection or an invalid address. -// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. -// */ -// ERR_FLASH_PROG_FAIL = 0x4B, -// /** -// * A fatal error has occurred while trying to erase the Flash possibly due to write protection. -// * Data in the Flash cannot be trusted after this error and it is possible this error is the result of exceeding the life cycles of the Flash. -// */ -// ERR_FLASH_ERASE_FAIL = 0x4C, - -// // Bootloader Errors -// /** The bootloader received an invalid message (failed attempt to go into bootloader). */ -// ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58, -// /** The bootloader received an invalid message (failed attempt to go into the bootloader). */ -// ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59, -// /** The bootloader cannot complete the bootload operation because either an image was not found or the image exceeded memory bounds. */ -// ERR_BOOTLOADER_NO_IMAGE = 0x05A, - -// // Transport Errors -// /** The APS layer attempted to send or deliver a message and failed. */ -// DELIVERY_FAILED = 0x66, -// /** This binding index is out of range for the current binding table. */ -// BINDING_INDEX_OUT_OF_RANGE = 0x69, -// /** This address table index is out of range for the current address table. */ -// ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A, -// /** An invalid binding table index was given to a function. */ -// INVALID_BINDING_INDEX = 0x6C, -// /** The API call is not allowed given the current state of the stack. */ -// INVALID_CALL = 0x70, -// /** The link cost to a node is not known. */ -// COST_NOT_KNOWN = 0x71, -// /** The maximum number of in-flight messages = i.e., ::EMBER_APS_UNICAST_MESSAGE_COUNT, has been reached. */ -// MAX_MESSAGE_LIMIT_REACHED = 0x72, -// /** The message to be transmitted is too big to fit into a single over-the-air packet. */ -// MESSAGE_TOO_LONG = 0x74, -// /** The application is trying to delete or overwrite a binding that is in use. */ -// BINDING_IS_ACTIVE = 0x75, -// /** The application is trying to overwrite an address table entry that is in use. */ -// ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76, -// /** An attempt was made to transmit during the suspend period. */ -// TRANSMISSION_SUSPENDED = 0x77, - -// // Green Power status codes -// /** Security match. */ -// MATCH = 0x78, -// /** Drop frame. */ -// DROP_FRAME = 0x79, -// /** */ -// PASS_UNPROCESSED = 0x7A, -// /** */ -// TX_THEN_DROP = 0x7B, -// /** */ -// NO_SECURITY = 0x7C, -// /** */ -// COUNTER_FAILURE = 0x7D, -// /** */ -// AUTH_FAILURE = 0x7E, -// /** */ -// UNPROCESSED = 0x7F, - -// // HAL Module Errors -// /** The conversion is complete. */ -// ADC_CONVERSION_DONE = 0x80, -// /** The conversion cannot be done because a request is being processed. */ -// ADC_CONVERSION_BUSY = 0x81, -// /** The conversion is deferred until the current request has been processed. */ -// ADC_CONVERSION_DEFERRED = 0x82, -// /** No results are pending. */ -// ADC_NO_CONVERSION_PENDING = 0x84, -// /** Sleeping (for a duration) has been abnormally interrupted and exited prematurely. */ -// SLEEP_INTERRUPTED = 0x85, - -// // PHY Errors -// /** -// * The transmit attempt failed because the radio scheduler could not find a slot -// * to transmit this packet in or a higher priority event interrupted it. -// */ -// PHY_TX_SCHED_FAIL = 0x87, -// /** The transmit hardware buffer underflowed. */ -// PHY_TX_UNDERFLOW = 0x88, -// /** The transmit hardware did not finish transmitting a packet. */ -// PHY_TX_INCOMPLETE = 0x89, -// /** An unsupported channel setting was specified. */ -// PHY_INVALID_CHANNEL = 0x8A, -// /** An unsupported power setting was specified. */ -// PHY_INVALID_POWER = 0x8B, -// /** The requested operation cannot be completed because the radio is currently busy, either transmitting a packet or performing calibration. */ -// PHY_TX_BUSY = 0x8C, -// /** The transmit attempt failed because all CCA attempts indicated that the channel was busy. */ -// PHY_TX_CCA_FAIL = 0x8D, -// /** -// * The transmit attempt was blocked from going over the air. -// * Typically this is due to the Radio Hold Off (RHO) or Coexistence plugins as they can prevent transmits based on external signals. -// */ -// PHY_TX_BLOCKED = 0x8E, -// /** The expected ACK was received after the last transmission. */ -// PHY_ACK_RECEIVED = 0x8F, - -// // Return Codes Passed to emberStackStatusHandler() See also ::emberStackStatusHandler = ,. -// /** The stack software has completed initialization and is ready to send and receive packets over the air. */ -// NETWORK_UP = 0x90, -// /** The network is not operating. */ -// NETWORK_DOWN = 0x91, -// /** An attempt to join a network failed. */ -// JOIN_FAILED = 0x94, -// /** After moving, a mobile node's attempt to re-establish contact with the network failed. */ -// MOVE_FAILED = 0x96, -// /** -// * An attempt to join as a router failed due to a Zigbee versus Zigbee Pro incompatibility. -// * Zigbee devices joining Zigbee Pro networks (or vice versa) must join as End Devices, not Routers. -// */ -// CANNOT_JOIN_AS_ROUTER = 0x98, -// /** The local node ID has changed. The application can get the new node ID by calling ::emberGetNodeId(). */ -// NODE_ID_CHANGED = 0x99, -// /** The local PAN ID has changed. The application can get the new PAN ID by calling ::emberGetPanId(). */ -// PAN_ID_CHANGED = 0x9A, -// /** The channel has changed. */ -// CHANNEL_CHANGED = 0x9B, -// /** The network has been opened for joining. */ -// NETWORK_OPENED = 0x9C, -// /** The network has been closed for joining. */ -// NETWORK_CLOSED = 0x9D, -// /** An attempt to join or rejoin the network failed because no router beacons could be heard by the joining node. */ -// NO_BEACONS = 0xAB, -// /** -// * An attempt was made to join a Secured Network using a pre-configured key, but the Trust Center sent back a -// * Network Key in-the-clear when an encrypted Network Key was required. (::EMBER_REQUIRE_ENCRYPTED_KEY). -// */ -// RECEIVED_KEY_IN_THE_CLEAR = 0xAC, -// /** An attempt was made to join a Secured Network, but the device did not receive a Network Key. */ -// NO_NETWORK_KEY_RECEIVED = 0xAD, -// /** After a device joined a Secured Network, a Link Key was requested (::EMBER_GET_LINK_KEY_WHEN_JOINING) but no response was ever received. */ -// NO_LINK_KEY_RECEIVED = 0xAE, -// /** -// * An attempt was made to join a Secured Network without a pre-configured key, -// * but the Trust Center sent encrypted data using a pre-configured key. -// */ -// PRECONFIGURED_KEY_REQUIRED = 0xAF, - -// // Security Errors -// /** The passed key data is not valid. A key of all zeros or all F's are reserved values and cannot be used. */ -// KEY_INVALID = 0xB2, -// /** The chosen security level (the value of ::EMBER_SECURITY_LEVEL) is not supported by the stack. */ -// INVALID_SECURITY_LEVEL = 0x95, -// /** -// * An error occurred when trying to encrypt at the APS Level. -// * -// * To APS encrypt an outgoing packet, the sender -// * needs to know the EUI64 of the destination. This error occurs because -// * the EUI64 of the destination can't be determined from -// * the short address (no entry in the neighbor, child, binding -// * or address tables). -// * -// * Every time this error code is seen, note that the stack initiates an -// * IEEE address discovery request behind the scenes. Responses -// * to the request are stored in the trust center cache portion of the -// * address table. Note that you need at least 1 entry allocated for -// * TC cache in the address table plugin. Depending on the available rows in -// * the table, newly discovered addresses may replace old ones. The address -// * table plugin is enabled by default on the host. If you are using an SoC -// * platform, please be sure to add the address table plugin. -// * -// * When customers choose to send APS messages by using short addresses, -// * they should incorporate a retry mechanism and try again, no sooner than -// * 2 seconds later, to resend the APS message. If the app always -// * receives 0xBE (IEEE_ADDRESS_DISCOVERY_IN_PROGRESS) after -// * multiple retries, that might indicate that: -// * a) destination node is not on the network -// * b) there are problems with the health of the network -// * c) there may not be any space set aside in the address table for -// * the newly discovered address - this can be rectified by reserving -// * more entries for the trust center cache in the address table plugin -// */ -// IEEE_ADDRESS_DISCOVERY_IN_PROGRESS = 0xBE, -// /** -// * An error occurred when trying to encrypt at the APS Level. -// * -// * This error occurs either because the long address of the recipient can't be -// * determined from the short address (no entry in the binding table) -// * or there is no link key entry in the table associated with the destination, -// * or there was a failure to load the correct key into the encryption core. -// */ -// APS_ENCRYPTION_ERROR = 0xA6, -// /** There was an attempt to form or join a network with security without calling ::emberSetInitialSecurityState() first. */ -// SECURITY_STATE_NOT_SET = 0xA8, -// /** -// * There was an attempt to set an entry in the key table using an invalid long address. Invalid addresses include: -// * - The local device's IEEE address -// * - Trust Center's IEEE address -// * - An existing table entry's IEEE address -// * - An address consisting of all zeros or all F's -// */ -// KEY_TABLE_INVALID_ADDRESS = 0xB3, -// /** There was an attempt to set a security configuration that is not valid given the other security settings. */ -// SECURITY_CONFIGURATION_INVALID = 0xB7, -// /** -// * There was an attempt to broadcast a key switch too quickly after broadcasting the next network key. -// * The Trust Center must wait at least a period equal to the broadcast timeout so that all routers have a chance -// * to receive the broadcast of the new network key. -// */ -// TOO_SOON_FOR_SWITCH_KEY = 0xB8, -// /** The received signature corresponding to the message that was passed to the CBKE Library failed verification and is not valid. */ -// SIGNATURE_VERIFY_FAILURE = 0xB9, -// /** -// * The message could not be sent because the link key corresponding to the destination is not authorized for use in APS data messages. -// * APS Commands (sent by the stack) are allowed. -// * To use it for encryption of APS data messages it must be authorized using a key agreement protocol (such as CBKE). -// */ -// KEY_NOT_AUTHORIZED = 0xBB, -// /** The security data provided was not valid, or an integrity check failed. */ -// SECURITY_DATA_INVALID = 0xBD, - -// // Miscellaneous Network Errors -// /** The node has not joined a network. */ -// NOT_JOINED = 0x93, -// /** A message cannot be sent because the network is currently overloaded. */ -// NETWORK_BUSY = 0xA1, -// /** The application tried to send a message using an endpoint that it has not defined. */ -// INVALID_ENDPOINT = 0xA3, -// /** The application tried to use a binding that has been remotely modified and the change has not yet been reported to the application. */ -// BINDING_HAS_CHANGED = 0xA4, -// /** An attempt to generate random bytes failed because of insufficient random data from the radio. */ -// INSUFFICIENT_RANDOM_DATA = 0xA5, -// /** A Zigbee route error command frame was received indicating that a source routed message from this node failed en route. */ -// SOURCE_ROUTE_FAILURE = 0xA9, -// /** -// * A Zigbee route error command frame was received indicating that a message sent to this node along a many-to-one route failed en route. -// * The route error frame was delivered by an ad-hoc search for a functioning route. -// */ -// MANY_TO_ONE_ROUTE_FAILURE = 0xAA, - -// // Miscellaneous Utility Errors -// /** -// * A critical and fatal error indicating that the version of the -// * stack trying to run does not match with the chip it's running on. The -// * software (stack) on the chip must be replaced with software -// * compatible with the chip. -// */ -// STACK_AND_HARDWARE_MISMATCH = 0xB0, -// /** An index was passed into the function that was larger than the valid range. */ -// INDEX_OUT_OF_RANGE = 0xB1, -// /** There are no empty entries left in the table. */ -// TABLE_FULL = 0xB4, -// /** The requested table entry has been erased and contains no valid data. */ -// TABLE_ENTRY_ERASED = 0xB6, -// /** The requested function cannot be executed because the library that contains the necessary functionality is not present. */ -// LIBRARY_NOT_PRESENT = 0xB5, -// /** The stack accepted the command and is currently processing the request. The results will be returned via an appropriate handler. */ -// OPERATION_IN_PROGRESS = 0xBA, -// /** -// * The EUI of the Trust center has changed due to a successful rejoin. -// * The device may need to perform other authentication to verify the new TC is authorized to take over. -// */ -// TRUST_CENTER_EUI_HAS_CHANGED = 0xBC, -// /** Trust center swapped out. The EUI has changed. */ -// TRUST_CENTER_SWAPPED_OUT_EUI_HAS_CHANGED = TRUST_CENTER_EUI_HAS_CHANGED, -// /** Trust center swapped out. The EUI has not changed. */ -// TRUST_CENTER_SWAPPED_OUT_EUI_HAS_NOT_CHANGED = 0xBF, - -// // NVM3 Token Errors -// /** NVM3 is telling the application that the initialization was aborted as no valid NVM3 page was found. */ -// NVM3_TOKEN_NO_VALID_PAGES = 0xC0, -// /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance was already opened with other parameters. */ -// NVM3_ERR_OPENED_WITH_OTHER_PARAMETERS = 0xC1, -// /** NVM3 is telling the application that the initialization was aborted as the NVM3 instance is not aligned properly in memory. */ -// NVM3_ERR_ALIGNMENT_INVALID = 0xC2, -// /** NVM3 is telling the application that the initialization was aborted as the size of the NVM3 instance is too small. */ -// NVM3_ERR_SIZE_TOO_SMALL = 0xC3, -// /** NVM3 is telling the application that the initialization was aborted as the NVM3 page size is not supported. */ -// NVM3_ERR_PAGE_SIZE_NOT_SUPPORTED = 0xC4, -// /** NVM3 is telling the application that there was an error initializing some of the tokens. */ -// NVM3_ERR_TOKEN_INIT = 0xC5, -// /** NVM3 is telling the application there has been an error when attempting to upgrade SimEE tokens. */ -// NVM3_ERR_UPGRADE = 0xC6, -// /** NVM3 is telling the application that there has been an unknown error. */ -// NVM3_ERR_UNKNOWN = 0xC7, - -// // Application Errors. These error codes are available for application use. -// /** -// * This error is reserved for customer application use. -// * This will never be returned from any portion of the network stack or HAL. -// */ -// APPLICATION_ERROR_0 = 0xF0, -// APPLICATION_ERROR_1 = 0xF1, -// APPLICATION_ERROR_2 = 0xF2, -// APPLICATION_ERROR_3 = 0xF3, -// APPLICATION_ERROR_4 = 0xF4, -// APPLICATION_ERROR_5 = 0xF5, -// APPLICATION_ERROR_6 = 0xF6, -// APPLICATION_ERROR_7 = 0xF7, -// APPLICATION_ERROR_8 = 0xF8, -// APPLICATION_ERROR_9 = 0xF9, -// APPLICATION_ERROR_10 = 0xFA, -// APPLICATION_ERROR_11 = 0xFB, -// APPLICATION_ERROR_12 = 0xFC, -// APPLICATION_ERROR_13 = 0xFD, -// APPLICATION_ERROR_14 = 0xFE, -// APPLICATION_ERROR_15 = 0xFF, -// }; - /** Status values used by EZSP. */ export enum EzspStatus { /** Success. */ From 626cc95d838e3e058a19546e683f1f1a9c4d10b3 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:47:27 +0200 Subject: [PATCH 04/17] Fix regression previous commit. --- src/adapter/ember/ezsp/ezsp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index b6f6b8d32b..d1a2034fcd 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -2926,7 +2926,7 @@ export class Ezsp extends EventEmitter { this.startCommand(EzspFrameID.JOIN_NETWORK_DIRECTLY); this.buffalo.writeUInt8(localNodeType); this.buffalo.writeEmberBeaconData(beacon); - this.buffalo.writeUInt8(radioTxPower); + this.buffalo.writeInt8(radioTxPower); this.buffalo.writeUInt8(clearBeaconsAfterNetworkUp ? 1 : 0); const sendStatus = await this.sendCommand(); From 1e1e3ece8af51b92ba7a30e90c1412eacc638a97 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:51:14 +0200 Subject: [PATCH 05/17] Update ezsp comments. Fixes/cleanup. --- src/adapter/ember/adapter/requestQueue.ts | 2 +- src/adapter/ember/ezsp/buffalo.ts | 89 ++-- src/adapter/ember/ezsp/ezsp.ts | 614 ++++++++++------------ test/adapter/ember/ezspBuffalo.test.ts | 36 ++ 4 files changed, 370 insertions(+), 371 deletions(-) diff --git a/src/adapter/ember/adapter/requestQueue.ts b/src/adapter/ember/adapter/requestQueue.ts index 8d6236c8a2..8bc08c586c 100644 --- a/src/adapter/ember/adapter/requestQueue.ts +++ b/src/adapter/ember/adapter/requestQueue.ts @@ -60,7 +60,7 @@ export class EmberRequestQueue { public startDispatching(): void { this.dispatching = true; - setTimeout(this.dispatch.bind(this), 0); + setTimeout(this.dispatch.bind(this), 1); logger.info(`Request dispatching started.`, NS); } diff --git a/src/adapter/ember/ezsp/buffalo.ts b/src/adapter/ember/ezsp/buffalo.ts index 324d7b7401..cbefb89977 100644 --- a/src/adapter/ember/ezsp/buffalo.ts +++ b/src/adapter/ember/ezsp/buffalo.ts @@ -85,42 +85,45 @@ import {EzspFrameID} from "./enums"; /** * Handle EmberStatus deprecation in v14+ for previous versions */ -const EMBER_TO_SL_STATUS_MAP: Partial> = { - 25: SLStatus.ZIGBEE_PACKET_HANDOFF_DROPPED, - 102: SLStatus.ZIGBEE_DELIVERY_FAILED, - 114: SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED, - 116: SLStatus.MESSAGE_TOO_LONG, - 117: SLStatus.ZIGBEE_BINDING_IS_ACTIVE, - 118: SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE, - 144: SLStatus.NETWORK_UP, - 145: SLStatus.NETWORK_DOWN, - 147: SLStatus.NOT_JOINED, - 149: SLStatus.ZIGBEE_INVALID_SECURITY_LEVEL, - 150: SLStatus.ZIGBEE_MOVE_FAILED, - 153: SLStatus.ZIGBEE_NODE_ID_CHANGED, - 154: SLStatus.ZIGBEE_PAN_ID_CHANGED, - 155: SLStatus.ZIGBEE_CHANNEL_CHANGED, - 156: SLStatus.ZIGBEE_NETWORK_OPENED, - 157: SLStatus.ZIGBEE_NETWORK_CLOSED, - 161: SLStatus.BUSY, - 164: SLStatus.ZIGBEE_BINDING_HAS_CHANGED, - 166: SLStatus.ZIGBEE_APS_ENCRYPTION_ERROR, - 165: SLStatus.ZIGBEE_INSUFFICIENT_RANDOM_DATA, - 169: SLStatus.ZIGBEE_SOURCE_ROUTE_FAILURE, - 168: SLStatus.ZIGBEE_SECURITY_STATE_NOT_SET, - 170: SLStatus.ZIGBEE_MANY_TO_ONE_ROUTE_FAILURE, - 172: SLStatus.ZIGBEE_RECEIVED_KEY_IN_THE_CLEAR, - 173: SLStatus.ZIGBEE_NO_NETWORK_KEY_RECEIVED, - 174: SLStatus.ZIGBEE_NO_LINK_KEY_RECEIVED, - 175: SLStatus.ZIGBEE_PRECONFIGURED_KEY_REQUIRED, - 176: SLStatus.ZIGBEE_STACK_AND_HARDWARE_MISMATCH, - 184: SLStatus.ZIGBEE_TOO_SOON_FOR_SWITCH_KEY, - 185: SLStatus.ZIGBEE_SIGNATURE_VERIFY_FAILURE, - 187: SLStatus.ZIGBEE_KEY_NOT_AUTHORIZED, - 188: SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED, - 190: SLStatus.ZIGBEE_IEEE_ADDRESS_DISCOVERY_IN_PROGRESS, - 191: SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_NOT_CHANGED, -}; +const EMBER_TO_SL_STATUS_MAP: ReadonlyMap = new Map([ + [0x02/*BAD_ARGUMENT*/, SLStatus.INVALID_PARAMETER], + [0x18/*NO_BUFFERS*/, SLStatus.ALLOCATION_FAILED], + [0x19/*PACKET_HANDOFF_DROP_PACKET*/, SLStatus.ZIGBEE_PACKET_HANDOFF_DROPPED], + [0x66/*DELIVERY_FAILED*/, SLStatus.ZIGBEE_DELIVERY_FAILED], + [0x70/*INVALID_CALL*/, SLStatus.INVALID_STATE], + [0x72/*MAX_MESSAGE_LIMIT_REACHED*/, SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED], + [0x74/*MESSAGE_TOO_LONG*/, SLStatus.MESSAGE_TOO_LONG], + [0x75/*BINDING_IS_ACTIVE*/, SLStatus.ZIGBEE_BINDING_IS_ACTIVE], + [0x76/*ADDRESS_TABLE_ENTRY_IS_ACTIVE*/, SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE], + [0x90/*NETWORK_UP*/, SLStatus.NETWORK_UP], + [0x91/*NETWORK_DOWN*/, SLStatus.NETWORK_DOWN], + [0x93/*NOT_JOINED*/, SLStatus.NOT_JOINED], + [0x95/*INVALID_SECURITY_LEVEL*/, SLStatus.ZIGBEE_INVALID_SECURITY_LEVEL], + [0x96/*MOVE_FAILED*/, SLStatus.ZIGBEE_MOVE_FAILED], + [0x99/*NODE_ID_CHANGED*/, SLStatus.ZIGBEE_NODE_ID_CHANGED], + [0x9A/*PAN_ID_CHANGED*/, SLStatus.ZIGBEE_PAN_ID_CHANGED], + [0x9B/*CHANNEL_CHANGED*/, SLStatus.ZIGBEE_CHANNEL_CHANGED], + [0x9C/*NETWORK_OPENED*/, SLStatus.ZIGBEE_NETWORK_OPENED], + [0x9D/*NETWORK_CLOSED*/, SLStatus.ZIGBEE_NETWORK_CLOSED], + [0xA1/*NETWORK_BUSY*/, SLStatus.BUSY], + [0xA4/*BINDING_HAS_CHANGED*/, SLStatus.ZIGBEE_BINDING_HAS_CHANGED], + [0xA5/*INSUFFICIENT_RANDOM_DATA*/, SLStatus.ZIGBEE_INSUFFICIENT_RANDOM_DATA], + [0xA6/*APS_ENCRYPTION_ERROR*/, SLStatus.ZIGBEE_APS_ENCRYPTION_ERROR], + [0xA9/*SOURCE_ROUTE_FAILURE*/, SLStatus.ZIGBEE_SOURCE_ROUTE_FAILURE], + [0xA8/*SECURITY_STATE_NOT_SET*/, SLStatus.ZIGBEE_SECURITY_STATE_NOT_SET], + [0xAA/*MANY_TO_ONE_ROUTE_FAILURE*/, SLStatus.ZIGBEE_MANY_TO_ONE_ROUTE_FAILURE], + [0xAC/*RECEIVED_KEY_IN_THE_CLEAR*/, SLStatus.ZIGBEE_RECEIVED_KEY_IN_THE_CLEAR], + [0xAD/*NO_NETWORK_KEY_RECEIVED*/, SLStatus.ZIGBEE_NO_NETWORK_KEY_RECEIVED], + [0xAE/*NO_LINK_KEY_RECEIVED*/, SLStatus.ZIGBEE_NO_LINK_KEY_RECEIVED], + [0xAF/*PRECONFIGURED_KEY_REQUIRED*/, SLStatus.ZIGBEE_PRECONFIGURED_KEY_REQUIRED], + [0xB0/*STACK_AND_HARDWARE_MISMATCH*/, SLStatus.ZIGBEE_STACK_AND_HARDWARE_MISMATCH], + [0xB8/*TOO_SOON_FOR_SWITCH_KEY*/, SLStatus.ZIGBEE_TOO_SOON_FOR_SWITCH_KEY], + [0xB9/*SIGNATURE_VERIFY_FAILURE*/, SLStatus.ZIGBEE_SIGNATURE_VERIFY_FAILURE], + [0xBB/*KEY_NOT_AUTHORIZED*/, SLStatus.ZIGBEE_KEY_NOT_AUTHORIZED], + [0xBC/*TRUST_CENTER_EUI_HAS_CHANGED*/, SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_CHANGED], + [0xBE/*IEEE_ADDRESS_DISCOVERY_IN_PROGRESS*/, SLStatus.ZIGBEE_IEEE_ADDRESS_DISCOVERY_IN_PROGRESS], + [0xBF/*TRUST_CENTER_SWAPPED_OUT_EUI_HAS_NOT_CHANGED*/, SLStatus.ZIGBEE_TRUST_CENTER_SWAP_EUI_HAS_NOT_CHANGED], +]); export class EzspBuffalo extends Buffalo { @@ -1319,12 +1322,18 @@ export class EzspBuffalo extends Buffalo { if (version < 0x0E) { const status = this.readUInt8(); - // map to SLStatus as appropriate + // skip lookup if SUCCESS (always zero) + if (status === SLStatus.OK) { + return status; + } + if (mapFromEmber) { - return EMBER_TO_SL_STATUS_MAP[status] ?? status; - } else { - return (status === SLStatus.OK) ? status : SLStatus.ZIGBEE_EZSP_ERROR; + // use mapped value, or pass as-is if none found + return EMBER_TO_SL_STATUS_MAP.get(status) ?? status; } + + // EzspStatus mapping to SLStatus is always same code + return SLStatus.ZIGBEE_EZSP_ERROR; } else { return this.readUInt32(); } diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index d1a2034fcd..2687ed1c4b 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -730,9 +730,8 @@ export class Ezsp extends EventEmitter { break; } case EzspFrameID.CUSTOM_FRAME_HANDLER: { - const payloadLength = this.buffalo.readUInt8(); - const payload = this.buffalo.readListUInt8(payloadLength); - this.ezspCustomFrameHandler(payloadLength, payload); + const payload = this.buffalo.readPayload(); + this.ezspCustomFrameHandler(payload); break; } case EzspFrameID.STACK_STATUS_HANDLER: { @@ -1404,10 +1403,9 @@ export class Ezsp extends EventEmitter { * * @param desiredProtocolVersion uint8_t The EZSP version the Host wishes to use. * To successfully set the version and allow other commands, this must be same as EZSP_PROTOCOL_VERSION. - * @return - * - uint8_t The EZSP version the NCP is using. - * - uint8_t * The type of stack running on the NCP (2). - * - uint16_t * The version number of the stack. + * @returns uint8_t The EZSP version the NCP is using. + * @returns uint8_t * The type of stack running on the NCP (2). + * @returns uint16_t * The version number of the stack. */ async ezspVersion(desiredProtocolVersion: number): Promise<[protocolVersion: number, stackType: number, stackVersion: number]> { this.startCommand(EzspFrameID.VERSION); @@ -1431,9 +1429,9 @@ export class Ezsp extends EventEmitter { * * @param configId Identifies which configuration value to read. * @returns - * - EzspStatus.SUCCESS if the value was read successfully, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize configId. - * - uint16_t * The configuration value. + * - SLStatus.OK if the value was read successfully, + * - SLStatus.ZIGBEE_EZSP_ERROR (for SL_ZIGBEE_EZSP_ERROR_INVALID_ID) if the NCP does not recognize configId. + * @returns uint16_t * The configuration value. */ async ezspGetConfigurationValue(configId: EzspConfigId): Promise<[SLStatus, value: number]> { this.startCommand(EzspFrameID.GET_CONFIGURATION_VALUE); @@ -1460,11 +1458,11 @@ export class Ezsp extends EventEmitter { * @param configId Identifies which configuration value to change. * @param value uint16_t The new configuration value. * @returns EzspStatus - * - EzspStatus.SUCCESS if the configuration value was changed, - * - EzspStatus.ERROR_OUT_OF_MEMORY if the new value exceeded the available memory, - * - EzspStatus.ERROR_INVALID_VALUE if the new value was out of bounds, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize configId, - * - EzspStatus.ERROR_INVALID_CALL if configuration values can no longer be modified. + * - SLStatus.OK if the configuration value was changed, + * - SLStatus.ZIGBEE_EZSP_ERROR if the new value exceeded the available memory, + * if the new value was out of bounds, + * if the NCP does not recognize configId, + * if configuration values can no longer be modified. */ async ezspSetConfigurationValue(configId: EzspConfigId, value: number): Promise { this.startCommand(EzspFrameID.SET_CONFIGURATION_VALUE); @@ -1489,11 +1487,10 @@ export class Ezsp extends EventEmitter { * @param attributeId uint16_t Attribute ID. * @param mask uint8_t Mask. * @param manufacturerCode uint16_t Manufacturer code. - * @returns - * - An EmberStatus value indicating success or the reason for failure. - * - uint8_t * Attribute data type. - * - uint8_t * Length of attribute data. - * - uint8_t * Attribute data. + * @returns An sl_zigbee_af_status_t value indicating success or the reason for failure, handled by the EZSP layer as a uint8_t. + * @returns uint8_t * Attribute data type. + * @returns uint8_t * Length of attribute data. + * @returns uint8_t * Attribute data. */ async ezspReadAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, readLength: number): Promise<[SLStatus, dataType: number, outReadLength: number, data: number[]]> { @@ -1535,7 +1532,7 @@ export class Ezsp extends EventEmitter { * @param justTest Override read only and data type. * @param dataType uint8_t Attribute data type. * @param data uint8_t * Attribute data. - * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. + * @returns An sl_zigbee_af_status_t value indicating success or the reason for failure. */ async ezspWriteAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, overrideReadOnlyAndDataType: boolean, justTest: boolean, dataType: number, data: Buffer): Promise { @@ -1556,7 +1553,7 @@ export class Ezsp extends EventEmitter { throw new EzspError(sendStatus); } - const status = this.buffalo.readUInt8();// XXX: not yet switched to uint32 in v14 + const status = this.buffalo.readStatus(0);// XXX: not yet switched to uint32 in v14, trick with 0 version for proper mapping return status; } @@ -1572,11 +1569,11 @@ export class Ezsp extends EventEmitter { * @param deviceVersion uint8_t The endpoint's device version. * @param inputClusterList uint16_t * Input cluster IDs the endpoint will accept. * @param outputClusterList uint16_t * Output cluster IDs the endpoint may send. - * @returns EzspStatus - * - EzspStatus.SUCCESS if the endpoint was added, - * - EzspStatus.ERROR_OUT_OF_MEMORY if there is not enough memory available to add the endpoint, - * - EzspStatus.ERROR_INVALID_VALUE if the endpoint already exists, - * - EzspStatus.ERROR_INVALID_CALL if endpoints can no longer be added. + * @returns + * - SLStatus.OK if the endpoint was added, + * - SLStatus.ZIGBEE_EZSP_ERROR if there is not enough memory available to add the endpoint, + * if the endpoint already exists, + * if endpoints can no longer be added. */ async ezspAddEndpoint(endpoint: number, profileId: number, deviceId: number, deviceVersion: number, inputClusterList: number[], outputClusterList: number[]): Promise { @@ -1607,8 +1604,8 @@ export class Ezsp extends EventEmitter { * @param policyId Identifies which policy to modify. * @param decisionId The new decision for the specified policy. * @returns - * - EzspStatus.SUCCESS if the policy was changed, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize policyId. + * - SLStatus.OK if the policy was changed, + * - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize policyId. */ async ezspSetPolicy(policyId: EzspPolicyId, decisionId: number): Promise { this.startCommand(EzspFrameID.SET_POLICY); @@ -1630,9 +1627,9 @@ export class Ezsp extends EventEmitter { * Allows the Host to read the policies used by the NCP to make fast decisions. * @param policyId Identifies which policy to read. * @returns - * - EzspStatus.SUCCESS if the policy was read successfully, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize policyId. - * - EzspDecisionId * The current decision for the specified policy. + * - SLStatus.OK if the policy was read successfully, + * - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize policyId. + * @returns EzspDecisionId * The current decision for the specified policy. */ async ezspGetPolicy(policyId: EzspPolicyId): Promise<[SLStatus, number]> { this.startCommand(EzspFrameID.GET_POLICY); @@ -1674,13 +1671,13 @@ export class Ezsp extends EventEmitter { * Reads a value from the NCP. * @param valueId Identifies which value to read. * @returns - * - EzspStatus.SUCCESS if the value was read successfully, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize valueId, - * - EzspStatus.ERROR_INVALID_VALUE if the length of the returned value exceeds the size of local storage allocated to receive it. - * - uint8_t * Both a command and response parameter. + * - SLStatus.OK if the value was read successfully, + * - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize valueId, + * if the length of the returned value exceeds the size of local storage allocated to receive it. + * @returns uint8_t * Both a command and response parameter. * On command, the maximum in bytes of local storage allocated to receive the returned value. * On response, the actual length in bytes of the returned value. - * - uint8_t * The value. + * @returns uint8_t * The value. */ async ezspGetValue(valueId: EzspValueId, valueLength: number): Promise<[SLStatus, outValueLength: number, outValue: number[]]> { @@ -1712,13 +1709,13 @@ export class Ezsp extends EventEmitter { * @param valueId Identifies which extended value ID to read. * @param characteristics uint32_t Identifies which characteristics of the extended value ID to read. These are specific to the value being read. * @returns - * - EzspStatus.SUCCESS if the value was read successfully, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize valueId, - * - EzspStatus.ERROR_INVALID_VALUE if the length of the returned value exceeds the size of local storage allocated to receive it. - * - uint8_t * Both a command and response parameter. + * - SLStatus.OK if the value was read successfully, + * - SLStatus.ZIGBEE_EZSP_ERROR if the NCP does not recognize valueId, + * if the length of the returned value exceeds the size of local storage allocated to receive it. + * @returns uint8_t * Both a command and response parameter. * On command, the maximum in bytes of local storage allocated to receive the returned value. * On response, the actual length in bytes of the returned value. - * - uint8_t * The value. + * @returns uint8_t * The value. */ async ezspGetExtendedValue(valueId: EzspExtendedValueId, characteristics: number, valueLength: number): Promise<[SLStatus, outValueLength: number, outValue: number[]]> { @@ -1752,11 +1749,11 @@ export class Ezsp extends EventEmitter { * @param valueId Identifies which value to change. * @param valueLength uint8_t The length of the value parameter in bytes. * @param value uint8_t * The new value. - * @returns EzspStatus - * - EzspStatus.SUCCESS if the value was changed, - * - EzspStatus.ERROR_INVALID_VALUE if the new value was out of bounds, - * - EzspStatus.ERROR_INVALID_ID if the NCP does not recognize valueId, - * - EzspStatus.ERROR_INVALID_CALL if the value could not be modified. + * @returns + * - SLStatus.OK if the value was changed, + * - SLStatus.ZIGBEE_EZSP_ERROR if the new value was out of bounds, + * if the NCP does not recognize valueId, + * if the value could not be modified. */ async ezspSetValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { this.startCommand(EzspFrameID.SET_VALUE); @@ -1776,12 +1773,11 @@ export class Ezsp extends EventEmitter { } /** - * Allows the Host to control the broadcast behaviour of a routing device used - * by the NCP. + * Allows the Host to control the broadcast behaviour of a routing device used by the NCP. * @param config uint8_t Passive ack config enum. * @param minAcksNeeded uint8_t The minimum number of acknowledgments (re-broadcasts) to wait for until * deeming the broadcast transmission complete. - * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetPassiveAckConfig(config: number, minAcksNeeded: number): Promise { this.startCommand(EzspFrameID.SET_PASSIVE_ACK_CONFIG); @@ -1929,12 +1925,10 @@ export class Ezsp extends EventEmitter { } /** - * Variable length data from the Host is echoed back by the NCP. This command - * has no other effects and is designed for testing the link between the Host and NCP. + * Variable length data from the Host is echoed back by the NCP. + * This command has no other effects and is designed for testing the link between the Host and NCP. * @param data uint8_t * The data to be echoed back. - * @returns - * - The length of the echo parameter in bytes. - * - echo uint8_t * The echo of the data. + * @returns uint8_t * The echo of the data. */ async ezspEcho(data: Buffer): Promise { this.startCommand(EzspFrameID.ECHO); @@ -1982,7 +1976,7 @@ export class Ezsp extends EventEmitter { * Sets a token (8 bytes of non-volatile storage) in the Simulated EEPROM of the NCP. * @param tokenId uint8_t Which token to set * @param tokenData uint8_t * The data to write to the token. - * @returns EmberStatus An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetToken(tokenId: number, tokenData: number[]): Promise { this.startCommand(EzspFrameID.SET_TOKEN); @@ -2003,9 +1997,8 @@ export class Ezsp extends EventEmitter { /** * Retrieves a token (8 bytes of non-volatile storage) from the Simulated EEPROM of the NCP. * @param tokenId uint8_t Which token to read - * @returns - * - An EmberStatus value indicating success or the reason for failure. - * - uint8_t * The contents of the token. + * @returns An SLStatus value indicating success or the reason for failure. + * @returns uint8_t * The contents of the token. */ async ezspGetToken(tokenId: number): Promise<[SLStatus, tokenData: number[]]> { this.startCommand(EzspFrameID.GET_TOKEN); @@ -2026,10 +2019,9 @@ export class Ezsp extends EventEmitter { /** * Retrieves a manufacturing token from the Flash Information Area of the NCP * (except for EZSP_STACK_CAL_DATA which is managed by the stack). - * @param Which manufacturing token to read. - * @returns - * - uint8_t The length of the tokenData parameter in bytes. - * - uint8_t * The manufacturing token data. + * @param tokenId Which manufacturing token to read. + * @returns uint8_t The length of the tokenData parameter in bytes. + * @returns uint8_t * The manufacturing token data. */ async ezspGetMfgToken(tokenId: EzspMfgTokenId): Promise<[number, tokenData: number[]]> { this.startCommand(EzspFrameID.GET_MFG_TOKEN); @@ -2096,7 +2088,7 @@ export class Ezsp extends EventEmitter { * EZSP_MFG_CBKE_DATA token. * @param tokenId Which manufacturing token to set. * @param tokenData uint8_t * The manufacturing token data. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetMfgToken(tokenId: EzspMfgTokenId, tokenData: Buffer): Promise { this.startCommand(EzspFrameID.SET_MFG_TOKEN); @@ -2125,9 +2117,8 @@ export class Ezsp extends EventEmitter { /** * Returns a pseudorandom number. - * @returns - * - Always returns EMBER_SUCCESS. - * - uint16_t * A pseudorandom number. + * @returns Always returns SLStatus.OK. + * @returns uint16_t * A pseudorandom number. */ async ezspGetRandomNumber(): Promise<[SLStatus, value: number]> { this.startCommand(EzspFrameID.GET_RANDOM_NUMBER); @@ -2153,7 +2144,7 @@ export class Ezsp extends EventEmitter { * This means that the actual delay will be between time and (time - 1). The maximum delay is 32767. * @param units The units for time. * @param repeat If true, a timerHandler callback will be generated repeatedly. If false, only a single timerHandler callback will be generated. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetTimer(timerId: number, time: number, units: EmberEventUnits, repeat: boolean): Promise { this.startCommand(EzspFrameID.SET_TIMER); @@ -2178,10 +2169,9 @@ export class Ezsp extends EventEmitter { * much longer it will be before a previously set timer will generate a * callback. * @param timerId uint8_t Which timer to get information about (0 or 1). - * @returns - * - uint16_t The delay before the timerHandler callback will be generated. - * - EmberEventUnits * The units for time. - * - bool * True if a timerHandler callback will be generated repeatedly. False if only a single timerHandler callback will be generated. + * @returns uint16_t The delay before the timerHandler callback will be generated. + * @returns EmberEventUnits * The units for time. + * @returns bool * True if a timerHandler callback will be generated repeatedly. False if only a single timerHandler callback will be generated. */ async ezspGetTimer(timerId: number): Promise<[number, units: EmberEventUnits, repeat: boolean]> { this.startCommand(EzspFrameID.GET_TIMER); @@ -2213,7 +2203,7 @@ export class Ezsp extends EventEmitter { * Sends a debug message from the Host to the Network Analyzer utility via the NCP. * @param binaryMessage true if the message should be interpreted as binary data, false if the message should be interpreted as ASCII text. * @param messageContents uint8_t * The binary message. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspDebugWrite(binaryMessage: boolean, messageContents: Buffer): Promise { this.startCommand(EzspFrameID.DEBUG_WRITE); @@ -2317,10 +2307,10 @@ export class Ezsp extends EventEmitter { * the response contains also the manufacturer ID and the version number of the * XNCP application that is running on the NCP. * @returns - * - EMBER_SUCCESS if the NCP is running the XNCP library, - * - EMBER_INVALID_CALL otherwise. - * - manufacturerId uint16_t * The manufactured ID the user has defined in the XNCP application. - * - versionNumber uint16_t * The version number of the XNCP application. + * - SLStatus.OK if the NCP is running the XNCP library. + * - SLStatus.INVALID_STATE otherwise. + * @returns manufacturerId uint16_t * The manufactured ID the user has defined in the XNCP application. + * @returns versionNumber uint16_t * The version number of the XNCP application. */ async ezspGetXncpInfo(): Promise<[SLStatus, manufacturerId: number, versionNumber: number]> { this.startCommand(EzspFrameID.GET_XNCP_INFO); @@ -2345,9 +2335,8 @@ export class Ezsp extends EventEmitter { * function. * @param uint8_t * The payload of the custom frame (maximum 119 bytes). * @param uint8_t The expected length of the response. - * @returns - * - The status returned by the custom command. - * - uint8_t *The response. + * @returns The status returned by the custom command. + * @returns uint8_t *The response. */ async ezspCustomFrame(payload: Buffer, replyLength: number): Promise<[SLStatus, outReply: Buffer]> { this.startCommand(EzspFrameID.CUSTOM_FRAME); @@ -2372,11 +2361,10 @@ export class Ezsp extends EventEmitter { /** * Callback * A callback indicating a custom EZSP message has been received. - * @param payloadLength uint8_t The length of the custom frame payload. * @param payload uint8_t * The payload of the custom frame. */ - ezspCustomFrameHandler(payloadLength: number, payload: number[]): void { - logger.debug(`ezspCustomFrameHandler(): callback called with: [payloadLength=${payloadLength}], [payload=${payload}]`, NS); + ezspCustomFrameHandler(payload: Buffer): void { + logger.debug(`ezspCustomFrameHandler(): callback called with: [payload=${payload.toString('hex')}]`, NS); } /** @@ -2680,9 +2668,10 @@ export class Ezsp extends EventEmitter { * of a network. EMBER_NOT_JOINED is returned if the node is not part of a * network. This command accepts options to control the network initialization. * @param networkInitStruct EmberNetworkInitStruct * An EmberNetworkInitStruct containing the options for initialization. - * @returns An EmberStatus value that indicates one of the following: successful - * initialization, EMBER_NOT_JOINED if the node is not part of a network, or the - * reason for failure. + * @returns + * - SLStatus.OK if successful initialization, + * - SLStatus.NOT_JOINED if the node is not part of a network + * - or the reason for failure. */ async ezspNetworkInit(networkInitStruct: EmberNetworkInitStruct): Promise { this.startCommand(EzspFrameID.NETWORK_INIT); @@ -2746,11 +2735,11 @@ export class Ezsp extends EventEmitter { * @param duration uint8_t Sets the exponent of the number of scan periods, where a scan period is 960 symbols. * The scan will occur for ((2^duration) + 1) scan periods. * @returns - * - SL_STATUS_OK signals that the scan successfully started. Possible error responses and their meanings: - * - SL_STATUS_MAC_SCANNING, we are already scanning; - * - SL_STATUS_BAD_SCAN_DURATION, we have set a duration value that is not 0..14 inclusive; - * - SL_STATUS_MAC_INCORRECT_SCAN_TYPE, we have requested an undefined scanning type; - * - SL_STATUS_INVALID_CHANNEL_MASK, our channel mask did not specify any valid channels. + * - SLStatus.OK signals that the scan successfully started. Possible error responses and their meanings: + * - SLStatus.MAC_SCANNING, we are already scanning; + * - SLStatus.BAD_SCAN_DURATION, we have set a duration value that is not 0..14 inclusive; + * - SLStatus.MAC_INCORRECT_SCAN_TYPE, we have requested an undefined scanning type; + * - SLStatus.INVALID_CHANNEL_MASK, our channel mask did not specify any valid channels. */ async ezspStartScan(scanType: EzspNetworkScanType, channelMask: number, duration: number): Promise { this.startCommand(EzspFrameID.START_SCAN); @@ -2797,10 +2786,8 @@ export class Ezsp extends EventEmitter { /** * Callback * @param channel uint8_t The channel on which the current error occurred. Undefined for the case of EMBER_SUCCESS. - * @param status The error condition that occurred on the current channel. Value will be EMBER_SUCCESS when the scan has completed. - * Returns the status of the current scan of type EZSP_ENERGY_SCAN or - * EZSP_ACTIVE_SCAN. EMBER_SUCCESS signals that the scan has completed. Other - * error conditions signify a failure to scan on the channel specified. + * @param status The error condition that occurred on the current channel. Value will be SLStatus.OK when the scan has completed. + * Other error conditions signify a failure to scan on the channel specified. */ ezspScanCompleteHandler(channel: number, status: SLStatus): void { logger.debug(`ezspScanCompleteHandler(): callback called with: [channel=${channel}], [status=${SLStatus[status]}]`, NS); @@ -2821,8 +2808,7 @@ export class Ezsp extends EventEmitter { * This function starts a series of scans which will return an available panId. * @param channelMask uint32_t The channels that will be scanned for available panIds. * @param duration uint8_t The duration of the procedure. - * @returns The error condition that occurred during the scan. Value will be - * EMBER_SUCCESS if there are no errors. + * @returns The error condition that occurred during the scan. Value will be SLStatus.OK if there are no errors. */ async ezspFindUnusedPanId(channelMask: number, duration: number): Promise { this.startCommand(EzspFrameID.FIND_UNUSED_PAN_ID); @@ -2842,7 +2828,7 @@ export class Ezsp extends EventEmitter { /** * Terminates a scan in progress. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspStopScan(): Promise { this.startCommand(EzspFrameID.STOP_SCAN); @@ -2861,7 +2847,7 @@ export class Ezsp extends EventEmitter { /** * Forms a new network by becoming the coordinator. * @param parameters EmberNetworkParameters * Specification of the new network. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspFormNetwork(parameters: EmberNetworkParameters): Promise { this.startCommand(EzspFrameID.FORM_NETWORK); @@ -2886,7 +2872,7 @@ export class Ezsp extends EventEmitter { * @param nodeType Specification of the role that this node will have in the network. * This role must not be EMBER_COORDINATOR. To be a coordinator, use the formNetwork command. * @param parameters EmberNetworkParameters * Specification of the network with which the node should associate. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspJoinNetwork(nodeType: EmberNodeType, parameters: EmberNetworkParameters): Promise { this.startCommand(EzspFrameID.JOIN_NETWORK); @@ -2919,7 +2905,7 @@ export class Ezsp extends EventEmitter { * @param beacon EmberBeaconData * Specifies the network with which the node should associate. * @param radioTxPower int8_t The radio transmit power to use, specified in dBm. * @param clearBeaconsAfterNetworkUp If true, clear beacons in cache upon join success. If join fail, do nothing. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspJoinNetworkDirectly(localNodeType: EmberNodeType, beacon: EmberBeaconData, radioTxPower: number, clearBeaconsAfterNetworkUp: boolean) : Promise { @@ -2945,7 +2931,7 @@ export class Ezsp extends EventEmitter { * stackStatusHandler callback to indicate that the network is down. The radio * will not be used until after sending a formNetwork or joinNetwork command. * @param options This parameter gives options when leave network - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspLeaveNetwork(options: EmberLeaveNetworkOption = EmberLeaveNetworkOption.WITH_NO_OPTION): Promise { this.startCommand(EzspFrameID.LEAVE_NETWORK); @@ -3015,7 +3001,7 @@ export class Ezsp extends EventEmitter { * their parent. Joining is initially disabled by default. * @param duration uint8_t A value of 0x00 disables joining. A value of 0xFF enables joining. * Any other value enables joining for that number of seconds. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspPermitJoining(duration: number): Promise { this.startCommand(EzspFrameID.PERMIT_JOINING); @@ -3056,7 +3042,7 @@ export class Ezsp extends EventEmitter { * @param scanDuration uint8_t How long to scan on each channel. * Allowed values are 0..5, with the scan times as specified by 802.15.4 (0 = 31ms, 1 = 46ms, 2 = 77ms, 3 = 138ms, 4 = 261ms, 5 = 507ms). * @param scanCount uint16_t The number of scans to be performed on each channel (1..8). - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspEnergyScanRequest(target: NodeId, scanChannels: number, scanDuration: number, scanCount: number): Promise { this.startCommand(EzspFrameID.ENERGY_SCAN_REQUEST); @@ -3078,7 +3064,7 @@ export class Ezsp extends EventEmitter { /** * Returns the current network parameters. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberNodeType * An EmberNodeType value indicating the current node type. * @returns EmberNetworkParameters * The current network parameters. */ @@ -3101,7 +3087,7 @@ export class Ezsp extends EventEmitter { /** * Returns the current radio parameters based on phy index. * @param phyIndex uint8_t Desired index of phy interface for radio parameters. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberMultiPhyRadioParameters * The current radio parameters based on provided phy index. */ async ezspGetRadioParameters(phyIndex: number): Promise<[SLStatus, parameters: EmberMultiPhyRadioParameters]> { @@ -3417,7 +3403,9 @@ export class Ezsp extends EventEmitter { /** * Returns information about a child of the local node. * @param uint8_t The index of the child of interest in the child table. Possible indexes range from zero to EMBER_CHILD_TABLE_SIZE. - * @returns EMBER_SUCCESS if there is a child at index. EMBER_NOT_JOINED if there is no child at index. + * @returns + * - SLStatus.OK if there is a child at index. + * - SLStatus.NOT_JOINED if there is no child at index. * @returns EmberChildData * The data of the child. */ async ezspGetChildData(index: number): Promise<[SLStatus, childData: EmberChildData]> { @@ -3440,7 +3428,9 @@ export class Ezsp extends EventEmitter { * Sets child data to the child table token. * @param index uint8_t The index of the child of interest in the child table. Possible indexes range from zero to (EMBER_CHILD_TABLE_SIZE - 1). * @param childData EmberChildData * The data of the child. - * @returns EMBER_SUCCESS if the child data is set successfully at index. EMBER_INDEX_OUT_OF_RANGE if provided index is out of range. + * @returns + * - SLStatus.OK if the child data is set successfully at index. + * - SLStatus.INVALID_INDEX if provided index is out of range. */ async ezspSetChildData(index: number, childData: EmberChildData): Promise { this.startCommand(EzspFrameID.SET_CHILD_DATA); @@ -3585,7 +3575,9 @@ export class Ezsp extends EventEmitter { * Returns information about a source route table entry * @param index uint8_t The index of the entry of interest in the source route table. * Possible indexes range from zero to SOURCE_ROUTE_TABLE_FILLED_SIZE. - * @returns EMBER_SUCCESS if there is source route entry at index. EMBER_NOT_FOUND if there is no source route at index. + * @returns + * - SLStatus.OK if there is source route entry at index. + * - SLStatus.NOT_FOUND if there is no source route at index. * @returns NodeId * The node ID of the destination in that entry. * @returns uint8_t * The closer node index for this source route table entry */ @@ -3611,8 +3603,9 @@ export class Ezsp extends EventEmitter { * neighbors can be obtained using the neighborCount command. * @param index uint8_t The index of the neighbor of interest. Neighbors are stored in ascending order by node id, * with all unused entries at the end of the table. - * @returns EMBER_ERR_FATAL if the index is greater or equal to the number of active neighbors, or if the device is an end device. - * Returns EMBER_SUCCESS otherwise. + * @returns + * - SLStatus.FAIL if the index is greater or equal to the number of active neighbors, or if the device is an end device. + * - SLStatus.OK otherwise. * @returns EmberNeighborTableEntry * The contents of the neighbor table entry. */ async ezspGetNeighbor(index: number): Promise<[SLStatus, value: EmberNeighborTableEntry]> { @@ -3637,7 +3630,9 @@ export class Ezsp extends EventEmitter { * frame counter as found in the Network Auxiliary header for the specified * neighbor or child * @param eui64 eui64 of the node - * @returns Return EMBER_NOT_FOUND if the node is not found in the neighbor or child table. Returns EMBER_SUCCESS otherwise + * @returns + * - SLStatus.NOT_FOUND if the node is not found in the neighbor or child table. + * - SLStatus.OK otherwise * @returns uint32_t * Return the frame counter of the node from the neighbor or child table */ async ezspGetNeighborFrameCounter(eui64: EUI64): Promise<[SLStatus, returnFrameCounter: number]> { @@ -3661,8 +3656,8 @@ export class Ezsp extends EventEmitter { * @param eui64 eui64 of the node * @param frameCounter uint32_t Return the frame counter of the node from the neighbor or child table * @returns - * - EMBER_NOT_FOUND if the node is not found in the neighbor or child table. - * - EMBER_SUCCESS otherwise + * - SLStatus.NOT_FOUND if the node is not found in the neighbor or child table. + * - SLStatus.OK otherwise */ async ezspSetNeighborFrameCounter(eui64: EUI64, frameCounter: number): Promise { this.startCommand(EzspFrameID.SET_NEIGHBOR_FRAME_COUNTER); @@ -3684,7 +3679,7 @@ export class Ezsp extends EventEmitter { * Sets the routing shortcut threshold to directly use a neighbor instead of * performing routing. * @param costThresh uint8_t The routing shortcut threshold to configure. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetRoutingShortcutThreshold(costThresh: number): Promise { this.startCommand(EzspFrameID.SET_ROUTING_SHORTCUT_THRESHOLD); @@ -3743,8 +3738,8 @@ export class Ezsp extends EventEmitter { * obtained using the getConfigurationValue command. * @param index uint8_t The index of the route table entry of interest. * @returns - * - EMBER_ERR_FATAL if the index is out of range or the device is an end - * - EMBER_SUCCESS otherwise. + * - SLStatus.FAIL if the index is out of range or the device is an end + * - SLStatus.OK otherwise. * @returns EmberRouteTableEntry * The contents of the route table entry. */ async ezspGetRouteTableEntry(index: number): Promise<[SLStatus, value: EmberRouteTableEntry]> { @@ -3772,7 +3767,7 @@ export class Ezsp extends EventEmitter { * with the node on which it is called. This can lead to disruption of existing * routes and erratic network behavior. * @param power int8_t Desired radio output power, in dBm. - * @returns An EmberStatus value indicating the success or failure of the command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspSetRadioPower(power: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_POWER); @@ -3795,7 +3790,7 @@ export class Ezsp extends EventEmitter { * communication module in your Developer Kit. Note: Care should be taken when * using this API, as all devices on a network must use the same channel. * @param channel uint8_t Desired radio channel. - * @returns An EmberStatus value indicating the success or failure of the command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspSetRadioChannel(channel: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_CHANNEL); @@ -3833,8 +3828,7 @@ export class Ezsp extends EventEmitter { /** * Set the configured 802.15.4 CCA mode in the radio. * @param ccaMode uint8_t A RAIL_IEEE802154_CcaMode_t value. - * @returns An EmberStatus value indicating the success or failure of the - * command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspSetRadioIeee802154CcaMode(ccaMode: number): Promise { this.startCommand(EzspFrameID.SET_RADIO_IEEE802154_CCA_MODE); @@ -3866,7 +3860,7 @@ export class Ezsp extends EventEmitter { * @param deliveryFailureThreshold uint8_t The number of APS delivery failures that will trigger a re-broadcast of the MTORR. * @param maxHops uint8_t The maximum number of hops that the MTORR broadcast will be allowed to have. * A value of 0 will be converted to the EMBER_MAX_HOPS value set by the stack. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetConcentrator(on: boolean, concentratorType: number, minTime: number, maxTime: number, routeErrorThreshold: number, deliveryFailureThreshold: number, maxHops: number): Promise { @@ -3890,6 +3884,11 @@ export class Ezsp extends EventEmitter { return status; } + /** + * Starts periodic many-to-one route discovery. + * Periodic discovery is started by default on bootup, but this function may be used if discovery + * has been stopped by a call to ::ezspConcentratorStopDiscovery(). + */ async ezspConcentratorStartDiscovery(): Promise { if (this.version < 0x0E) { throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); @@ -3904,6 +3903,9 @@ export class Ezsp extends EventEmitter { } } + /** + * Stops periodic many-to-one route discovery. + */ async ezspConcentratorStopDiscovery(): Promise { if (this.version < 0x0E) { throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); @@ -3918,6 +3920,11 @@ export class Ezsp extends EventEmitter { } } + /** + * Notes when a route error has occurred. + * @param status + * @param nodeId + */ async ezspConcentratorNoteRouteError(status: SLStatus, nodeId: NodeId): Promise { if (this.version < 0x0E) { throw new EzspError(EzspStatus.ERROR_INVALID_FRAME_ID); @@ -3937,8 +3944,7 @@ export class Ezsp extends EventEmitter { /** * Sets the error code that is sent back from a router with a broken route. * @param errorCode uint8_t Desired error code. - * @returns An EmberStatus value indicating the success or failure of the - * command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspSetBrokenRouteErrorCode(errorCode: number): Promise { this.startCommand(EzspFrameID.SET_BROKEN_ROUTE_ERROR_CODE); @@ -3964,7 +3970,7 @@ export class Ezsp extends EventEmitter { * @param channel uint8_t Desired radio channel. * @param power int8_t Desired radio output power, in dBm. * @param bitmask Network configuration bitmask. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspMultiPhyStart(phyIndex: number, page: number, channel: number, power: number, bitmask: EmberMultiPhyNwkConfig): Promise { this.startCommand(EzspFrameID.MULTI_PHY_START); @@ -3988,7 +3994,7 @@ export class Ezsp extends EventEmitter { /** * This causes to bring down the radio interface other than native. * @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspMultiPhyStop(phyIndex: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_STOP); @@ -4015,8 +4021,7 @@ export class Ezsp extends EventEmitter { * can lead to disruption of existing routes and erratic network behavior. * @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one. * @param power int8_t Desired radio output power, in dBm. - * @returns An EmberStatus value indicating the success or failure of the - * command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspMultiPhySetRadioPower(phyIndex: number, power: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_SET_RADIO_POWER); @@ -4036,7 +4041,7 @@ export class Ezsp extends EventEmitter { /** * Send Link Power Delta Request from a child to its parent - * @returns An EmberStatus value indicating the success or failure of sending the request. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspSendLinkPowerDeltaRequest(): Promise { this.startCommand(EzspFrameID.SEND_LINK_POWER_DELTA_REQUEST); @@ -4061,7 +4066,7 @@ export class Ezsp extends EventEmitter { * @param phyIndex uint8_t Index of phy interface. The native phy index would be always zero hence valid phy index starts from one. * @param page uint8_t Desired radio channel page. * @param channel uint8_t Desired radio channel. - * @returns An EmberStatus value indicating the success or failure of the command. + * @returns An SLStatus value indicating the success or failure of the command. */ async ezspMultiPhySetRadioChannel(phyIndex: number, page: number, channel: number): Promise { this.startCommand(EzspFrameID.MULTI_PHY_SET_RADIO_CHANNEL); @@ -4082,7 +4087,7 @@ export class Ezsp extends EventEmitter { /** * Obtains the current duty cycle state. - * @returns An EmberStatus value indicating the success or failure of the command. + * @returns An SLStatus value indicating the success or failure of the command. * @returns EmberDutyCycleState * The current duty cycle state in effect. */ async ezspGetDutyCycleState(): Promise<[SLStatus, returnedState: EmberDutyCycleState]> { @@ -4104,10 +4109,11 @@ export class Ezsp extends EventEmitter { * Set the current duty cycle limits configuration. The Default limits set by * stack if this call is not made. * @param limits EmberDutyCycleLimits * The duty cycle limits configuration to utilize. - * @returns EMBER_SUCCESS if the duty cycle limit configurations set - * successfully, EMBER_BAD_ARGUMENT if set illegal value such as setting only - * one of the limits to default or violates constraints Susp > Crit > Limi, - * EMBER_INVALID_CALL if device is operating on 2.4Ghz + * @returns + * - SLStatus.OK if the duty cycle limit configurations set successfully, + * - SLStatus.INVALID_PARAMETER if set illegal value such as setting only one of the limits to default + * or violates constraints Susp > Crit > Limi, + * - SLStatus.INVALID_STATE if device is operating on 2.4Ghz */ async ezspSetDutyCycleLimitsInStack(limits: EmberDutyCycleLimits): Promise { this.startCommand(EzspFrameID.SET_DUTY_CYCLE_LIMITS_IN_STACK); @@ -4125,10 +4131,9 @@ export class Ezsp extends EventEmitter { } /** - * Obtains the current duty cycle limits that were previously set by a call to - * emberSetDutyCycleLimitsInStack(), or the defaults set by the stack if no set - * call was made. - * @returns An EmberStatus value indicating the success or failure of the command. + * Obtains the current duty cycle limits that were previously set by a call to emberSetDutyCycleLimitsInStack(), + * or the defaults set by the stack if no set call was made. + * @returns An SLStatus value indicating the success or failure of the command. * @returns EmberDutyCycleLimits * Return current duty cycle limits if returnedLimits is not NULL */ async ezspGetDutyCycleLimits(): Promise<[SLStatus, returnedLimits: EmberDutyCycleLimits]> { @@ -4154,8 +4159,8 @@ export class Ezsp extends EventEmitter { * The passed pointer arrayOfDeviceDutyCycles MUST have space for maxDevices. * @param maxDevices uint8_t Number of devices to retrieve consumed duty cycle. * @returns - * - EMBER_SUCCESS if the duty cycles were read successfully, - * - EMBER_BAD_ARGUMENT maxDevices is greater than EMBER_MAX_END_DEVICE_CHILDREN + 1. + * - SLStatus.OK if the duty cycles were read successfully, + * - SLStatus.INVALID_PARAMETER maxDevices is greater than SL_ZIGBEE_MAX_END_DEVICE_CHILDREN + 1. * @returns uint8_t * Consumed duty cycles up to maxDevices. When the number of children that are being monitored is less than maxDevices, * the NodeId element in the EmberPerDeviceDutyCycle will be 0xFFFF. */ @@ -4195,9 +4200,8 @@ export class Ezsp extends EventEmitter { * Configure the number of beacons to store when issuing active scans for networks. * @param numBeacons uint8_t The number of beacons to cache when scanning. * @returns - * - SL_STATUS_INVALID_PARAMETER if numBeacons is greater than - * - SL_ZIGBEE_MAX_BEACONS_TO_STORE - * - SL_STATUS_OK + * - SLStatus.INVALID_PARAMETER if numBeacons is greater than SL_ZIGBEE_MAX_BEACONS_TO_STORE + * - SLStatus.OK */ async ezspSetNumBeaconToStore(numBeacons: number): Promise { if (this.version < 0x0E) { @@ -4221,7 +4225,7 @@ export class Ezsp extends EventEmitter { /** * Fetches the specified beacon in the cache. Beacons are stored in cache after issuing an active scan. * @param beaconNumber uint8_t The beacon index to fetch. Valid values range from 0 to ezspGetNumStoredBeacons-1. - * @returns An appropriate sl_status_t status code. + * @returns An appropriate SLStatus status code. * @returns EmberBeaconData * The beacon to populate upon success. */ async ezspGetStoredBeacon(beaconNumber: number): Promise<[SLStatus, beacon: EmberBeaconData]> { @@ -4264,6 +4268,7 @@ export class Ezsp extends EventEmitter { /** * Clears all cached beacons that have been collected from a scan. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspClearStoredBeacons(): Promise { this.startCommand(EzspFrameID.CLEAR_STORED_BEACONS); @@ -4287,7 +4292,7 @@ export class Ezsp extends EventEmitter { * This call sets the radio channel in the stack and propagates the information * to the hardware. * @param radioChannel uint8_t The radio channel to be set. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetLogicalAndRadioChannel(radioChannel: number): Promise { this.startCommand(EzspFrameID.SET_LOGICAL_AND_RADIO_CHANNEL); @@ -4309,7 +4314,7 @@ export class Ezsp extends EventEmitter { * If the network is using security, the device must call sli_zigbee_stack_set_initial_security_state() first. * @param parameters Specification of the new network. * @param initiator Whether this device is initiating or joining the network. - * @returns An sl_status_t value indicating success or a reason for failure. + * @returns An SLStatus value indicating success or a reason for failure. */ async ezspSleepyToSleepyNetworkStart(parameters: EmberNetworkParameters, initiator: boolean): Promise { if (this.version < 0x0E) { @@ -4516,7 +4521,7 @@ export class Ezsp extends EventEmitter { /** * Deletes all binding table entries. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspClearBindingTable(): Promise { this.startCommand(EzspFrameID.CLEAR_BINDING_TABLE); @@ -4536,7 +4541,7 @@ export class Ezsp extends EventEmitter { * Sets an entry in the binding table. * @param index uint8_t The index of a binding table entry. * @param value EmberBindingTableEntry * The contents of the binding entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetBinding(index: number, value: EmberBindingTableEntry): Promise { this.startCommand(EzspFrameID.SET_BINDING); @@ -4557,7 +4562,7 @@ export class Ezsp extends EventEmitter { /** * Gets an entry from the binding table. * @param index uint8_t The index of a binding table entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberBindingTableEntry * The contents of the binding entry. */ async ezspGetBinding(index: number): Promise<[SLStatus, value: EmberBindingTableEntry]> { @@ -4579,7 +4584,7 @@ export class Ezsp extends EventEmitter { /** * Deletes a binding table entry. * @param index uint8_t The index of a binding table entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspDeleteBinding(index: number): Promise { this.startCommand(EzspFrameID.DELETE_BINDING); @@ -4672,7 +4677,7 @@ export class Ezsp extends EventEmitter { * command. * @param entry EmberBindingTableEntry * The requested binding. * @param index uint8_t The index at which the binding was added. - * @param policyDecision SL_STATUS_OK if the binding was added to the table and any other status if not. + * @param policyDecision SLStatus.OK if the binding was added to the table and any other status if not. */ ezspRemoteSetBindingHandler(entry: EmberBindingTableEntry, index: number, policyDecision: SLStatus): void { logger.debug(`ezspRemoteSetBindingHandler(): callback called with: [entry=${entry}], [index=${index}], ` @@ -4686,7 +4691,7 @@ export class Ezsp extends EventEmitter { * but it can change the policy for future decisions using the setPolicy * command. * @param index uint8_t The index of the binding whose deletion was requested. - * @param policyDecision SL_STATUS_OK if the binding was removed from the table and any other status if not. + * @param policyDecision SLStatus.OK if the binding was removed from the table and any other status if not. */ ezspRemoteDeleteBindingHandler(index: number, policyDecision: SLStatus): void { logger.debug(`ezspRemoteDeleteBindingHandler(): callback called with: [index=${index}], [policyDecision=${SLStatus[policyDecision]}]`, NS); @@ -4738,7 +4743,7 @@ export class Ezsp extends EventEmitter { * @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host. * This value is used in the ezspMessageSentHandler response to refer to this message. * @param messageContents uint8_t * Content of the message. - * @returns An sl_status_t value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ async ezspSendUnicast(type: EmberOutgoingMessageType, indexOrDestination: NodeId, apsFrame: EmberApsFrame, messageTag: number, @@ -4778,7 +4783,7 @@ export class Ezsp extends EventEmitter { * A radius of zero is converted to EMBER_MAX_HOPS. * @param uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. * @param uint8_t * The broadcast message. - * @returns An sl_status_t value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ async ezspSendBroadcast(alias: NodeId, destination: NodeId, nwkSequence: number, apsFrame: EmberApsFrame, radius: number, messageTag: number, @@ -4817,7 +4822,7 @@ export class Ezsp extends EventEmitter { * Sends proxied broadcast message for another node in conjunction with sl_zigbee_proxy_broadcast * where a long source is also specified in the NWK frame control. * @param euiSource The long source from which to send the broadcast - * @returns An sl_status_t value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspProxyNextBroadcastFromLong(euiSource: EUI64): Promise { if (this.version < 0x0E) { @@ -4850,13 +4855,13 @@ export class Ezsp extends EventEmitter { * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. * @param messageLength uint8_t (v14+: uint16_t) The length of the messageContents parameter in bytes. * @param messageContents uint8_t * The multicast message. - * @returns An sl_status_t value. For any result other than SL_STATUS_OK, the message will not be sent. - * - SL_STATUS_OK - The message has been submitted for transmission. - * - SL_STATUS_INVALID_INDEX - The bindingTableIndex refers to a non-multicast binding. - * - SL_STATUS_NETWORK_DOWN - The node is not part of a network. - * - SL_STATUS_MESSAGE_TOO_LONG - The message is too large to fit in a MAC layer frame. - * - SL_STATUS_ALLOCATION_FAILED - The free packet buffer pool is empty. - * - SL_STATUS_BUSY - Insufficient resources available in Network or MAC layers to send message. + * @returns An SLStatus value. For any result other than SLStatus.OK, the message will not be sent. + * - SLStatus.OK - The message has been submitted for transmission. + * - SLStatus.INVALID_INDEX - The bindingTableIndex refers to a non-multicast binding. + * - SLStatus.NETWORK_DOWN - The node is not part of a network. + * - SLStatus.MESSAGE_TOO_LONG - The message is too large to fit in a MAC layer frame. + * - SLStatus.ALLOCATION_FAILED - The free packet buffer pool is empty. + * - SLStatus.BUSY - Insufficient resources available in Network or MAC layers to send message. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ async ezspSendMulticast(apsFrame: EmberApsFrame, hops: number, broadcastAddr: number, alias: NodeId, nwkSequence: number, messageTag: number, @@ -4898,17 +4903,17 @@ export class Ezsp extends EventEmitter { * @param uint8_t The length of the messageContents parameter in bytes. * @param uint8_t * The reply message. * @returns An EmberStatus value. - * - EMBER_INVALID_CALL - The EZSP_UNICAST_REPLIES_POLICY is set to EZSP_HOST_WILL_NOT_SUPPLY_REPLY. + * - SLStatus.INVALID_STATE - The SL_ZIGBEE_EZSP_UNICAST_REPLIES_POLICY is set to SL_ZIGBEE_EZSP_HOST_WILL_NOT_SUPPLY_REPLY. * This means the NCP will automatically send an empty reply. The Host must change - * the policy to EZSP_HOST_WILL_SUPPLY_REPLY before it can supply the reply. + * the policy to SL_ZIGBEE_EZSP_HOST_WILL_SUPPLY_REPLY before it can supply the reply. * There is one exception to this rule: In the case of responses to message * fragments, the host must call sendReply when a message fragment is received. * In this case, the policy set on the NCP does not matter. The NCP expects a * sendReply call from the Host for message fragments regardless of the current * policy settings. - * - EMBER_NO_BUFFERS - Not enough memory was available to send the reply. - * - EMBER_NETWORK_BUSY - Either no route or insufficient resources available. - * - EMBER_SUCCESS - The reply was successfully queued for transmission. + * - SLStatus.ALLOCATION_FAILED - Not enough memory was available to send the reply. + * - SLStatus.BUSY - Either no route or insufficient resources available. + * - SLStatus.OK - The reply was successfully queued for transmission. */ async ezspSendReply(sender: NodeId, apsFrame: EmberApsFrame, messageContents: Buffer): Promise { @@ -4986,8 +4991,9 @@ export class Ezsp extends EventEmitter { * The latter is used when the concentrator has insufficient RAM to store all outbound source routes. * In that case, route records are sent to the concentrator prior to every inbound APS unicast. * @param radius uint8_t The maximum number of hops the route request will be relayed. A radius of zero is converted to EMBER_MAX_HOPS - * @returns EMBER_SUCCESS if the route request was successfully submitted to the - * transmit queue, and EMBER_ERR_FATAL otherwise. + * @returns + * - SLStatus.OK if the route request was successfully submitted to the transmit queue, + * - SLStatus.FAIL otherwise. */ async ezspSendManyToOneRouteRequest(concentratorType: number, radius: number): Promise { this.startCommand(EzspFrameID.SEND_MANY_TO_ONE_ROUTE_REQUEST); @@ -5036,11 +5042,11 @@ export class Ezsp extends EventEmitter { /** * Callback * Indicates the result of a data poll to the parent of the local node. - * @param status An EmberStatus value: - * - EMBER_SUCCESS - Data was received in response to the poll. - * - EMBER_MAC_NO_DATA - No data was pending. - * - EMBER_DELIVERY_FAILED - The poll message could not be sent. - * - EMBER_MAC_NO_ACK_RECEIVED - The poll message was sent but not acknowledged by the parent. + * @param status An SLStatus value: + * - SLStatus.OK - Data was received in response to the poll. + * - SLStatus.MAC_NO_DATA - No data was pending. + * - SLStatus.ZIGBEE_DELIVERY_FAILED - The poll message could not be sent. + * - SLStatus.MAC_NO_ACK_RECEIVED - The poll message was sent but not acknowledged by the parent. */ ezspPollCompleteHandler(status: SLStatus): void { logger.debug(`ezspPollCompleteHandler(): callback called with: [status=${SLStatus[status]}]`, NS); @@ -5052,8 +5058,8 @@ export class Ezsp extends EventEmitter { * The message is sent from emberPollHandler, which is called when the child requests data. * @param childId The ID of the child that just polled for data. * @returns - * - SL_STATUS_OK - The next time that the child polls, it will be informed that it has pending data. - * - SL_STATUS_NOT_JOINED - The child identified by childId is not our child. + * - SLStatus.OK - The next time that the child polls, it will be informed that it has pending data. + * - SLStatus.NOT_JOINED - The child identified by childId is not our child. */ async ezspSetMessageFlag(childId: NodeId): Promise { if (this.version < 0x0E) { @@ -5079,8 +5085,8 @@ export class Ezsp extends EventEmitter { * The next time the child polls, it will be informed that it does not have any pending messages. * @param childId The ID of the child that no longer has pending messages. * @returns - * - SL_STATUS_OK - The next time that the child polls, it will be informed that it does not have any pending messages. - * - SL_STATUS_NOT_JOINED - The child identified by childId is not our child. + * - SLStatus.OK - The next time that the child polls, it will be informed that it does not have any pending messages. + * - SLStatus.NOT_JOINED - The child identified by childId is not our child. */ async ezspClearMessageFlag(childId: NodeId): Promise { if (this.version < 0x0E) { @@ -5119,8 +5125,8 @@ export class Ezsp extends EventEmitter { * @param longId The long ID of the node. * @param nodeType The nodetype e.g., SL_ZIGBEE_ROUTER defining, if this would be added to the child table or neighbor table. * @returns - * - SL_STATUS_OK - This node has been successfully added. - * - SL_STATUS_FAIL - The child was not added to the child/neighbor table. + * - SLStatus.OK - This node has been successfully added. + * - SLStatus.FAIL - The child was not added to the child/neighbor table. */ async ezspAddChild(shortId: NodeId, longId: EUI64, nodeType: EmberNodeType): Promise { if (this.version < 0x0E) { @@ -5148,8 +5154,8 @@ export class Ezsp extends EventEmitter { * This can affect the network functionality, and needs to be used wisely. * @param childEui64 The long ID of the node. * @returns - * - SL_STATUS_OK - This node has been successfully removed. - * - SL_STATUS_FAIL - The node was not found in either of the child or neighbor tables. + * - SLStatus.OK - This node has been successfully removed. + * - SLStatus.FAIL - The node was not found in either of the child or neighbor tables. */ async ezspRemoveChild(childEui64: EUI64): Promise { if (this.version < 0x0E) { @@ -5814,8 +5820,9 @@ export class Ezsp extends EventEmitter { * Supply a source route for the next outgoing message. * @param destination The destination of the source route. * @param relayList uint16_t * The source route. - * @returns EMBER_SUCCESS if the source route was successfully stored, and - * EMBER_NO_BUFFERS otherwise. + * @returns + * - SLStatus.OK if the source route was successfully stored, + * - SLStatus.ALLOCATION_FAILED otherwise. */ async ezspSetSourceRoute(destination: NodeId, relayList: number[]): Promise { this.startCommand(EzspFrameID.SET_SOURCE_ROUTE); @@ -5839,7 +5846,7 @@ export class Ezsp extends EventEmitter { * @param targetShort The destination node of the key. * @param targetLong The long address of the destination node. * @param parentShortId The parent node of the destination node. - * @returns EMBER_SUCCESS if send was successful + * @returns SLStatus.OK if send was successful */ async ezspUnicastCurrentNetworkKey(targetShort: NodeId, targetLong: EUI64, parentShortId: NodeId): Promise { this.startCommand(EzspFrameID.UNICAST_CURRENT_NETWORK_KEY); @@ -5893,7 +5900,9 @@ export class Ezsp extends EventEmitter { * @param addressTableIndex * @param eui64 * @param id - * @returns SL_STATUS_OK if the information was successfully set, and SL_STATUS_ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise. + * @returns + * - SLStatus.OK if the information was successfully set, + * - SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise. */ async ezspSetAddressTableInfo(addressTableIndex: number, eui64: EUI64, id: NodeId): Promise { if (this.version < 0x0E) { @@ -5919,7 +5928,7 @@ export class Ezsp extends EventEmitter { /** * Gets the EUI64 and short ID of an address table entry. * @param addressTableIndex - * @returns An sl_status_t value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns One of the following: * - The short ID corresponding to the remote node whose EUI64 is stored in the address table at the given index. * - SL_ZIGBEE_UNKNOWN_NODE_ID: @@ -5991,8 +6000,8 @@ export class Ezsp extends EventEmitter { * EMBER_INDIRECT_TRANSMISSION_TIMEOUT. * @param remoteEui64 The address of the node for which the timeout is to be returned. * @returns - * - SL_STATUS_OK if the retry interval will be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT - * - SL_STATUS_FAIL if the normal retry interval will be used. + * - SLStatus.OK if the retry interval will be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT + * - SLStatus.FAIL if the normal retry interval will be used. */ async ezspGetExtendedTimeout(remoteEui64: EUI64): Promise { this.startCommand(EzspFrameID.GET_EXTENDED_TIMEOUT); @@ -6022,18 +6031,18 @@ export class Ezsp extends EventEmitter { * @param addressTableIndex uint8_t The index of the address table entry that will be modified. * @param newEui64 The EUI64 to be written to the address table entry. * @param newId One of the following: The short ID corresponding to the new EUI64. - * EMBER_UNKNOWN_NODE_ID if the new EUI64 is valid but the short ID is unknown and should be discovered by the stack. - * EMBER_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry is now unused. - * @param newExtendedTimeout true if the retry interval should be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. + * SL_ZIGBEE_UNKNOWN_NODE_ID if the new EUI64 is valid but the short ID is unknown and should be discovered by the stack. + * SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry is now unused. + * @param newExtendedTimeout true if the retry interval should be increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT. * false if the normal retry interval should be used. - * @returns EMBER_SUCCESS if the EUI64, short ID and extended timeout setting - * were successfully modified, and EMBER_ADDRESS_TABLE_ENTRY_IS_ACTIVE - * otherwise. + * @returns + * - SLStatus.OK if the EUI64, short ID and extended timeout setting were successfully modified, + * - SLStatus.ZIGBEE_ADDRESS_TABLE_ENTRY_IS_ACTIVE otherwise. * @returns oldEui64 The EUI64 of the address table entry before it was modified. * @returns oldId NodeId * One of the following: The short ID corresponding to the EUI64 before it was modified. - * EMBER_UNKNOWN_NODE_ID if the short ID was unknown. EMBER_DISCOVERY_ACTIVE_NODE_ID if discovery of the short ID was underway. - * EMBER_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry was unused. - * @returns oldExtendedTimeouttrue bool * if the retry interval was being increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. + * SL_ZIGBEE_UNKNOWN_NODE_ID if the short ID was unknown. SL_ZIGBEE_DISCOVERY_ACTIVE_NODE_ID if discovery of the short ID was underway. + * SL_ZIGBEE_TABLE_ENTRY_UNUSED_NODE_ID if the address table entry was unused. + * @returns oldExtendedTimeouttrue bool * if the retry interval was being increased by SL_ZIGBEE_INDIRECT_TRANSMISSION_TIMEOUT. * false if the normal retry interval was being used. */ async ezspReplaceAddressTableEntry(addressTableIndex: number, newEui64: EUI64, newId: NodeId, newExtendedTimeout: boolean): @@ -6062,8 +6071,7 @@ export class Ezsp extends EventEmitter { * Returns the node ID that corresponds to the specified EUI64. The node ID is * found by searching through all stack tables for the specified EUI64. * @param eui64 The EUI64 of the node to look up. - * @returns The short ID of the node or EMBER_NULL_NODE_ID if the short ID is not - * known. + * @returns The short ID of the node or SL_ZIGBEE_NULL_NODE_ID if the short ID is not known. */ async ezspLookupNodeIdByEui64(eui64: EUI64): Promise { this.startCommand(EzspFrameID.LOOKUP_NODE_ID_BY_EUI64); @@ -6084,8 +6092,9 @@ export class Ezsp extends EventEmitter { * Returns the EUI64 that corresponds to the specified node ID. The EUI64 is * found by searching through all stack tables for the specified node ID. * @param nodeId The short ID of the node to look up. - * @returns EMBER_SUCCESS if the EUI64 was found, EMBER_ERR_FATAL if the EUI64 is - * not known. + * @returns + * - SLStatus.OK if the EUI64 was found, + * - SLStatus.FAIL if the EUI64 is not known. * @returns eui64 The EUI64 of the node. */ async ezspLookupEui64ByNodeId(nodeId: NodeId): Promise<[SLStatus, eui64: EUI64]> { @@ -6107,7 +6116,7 @@ export class Ezsp extends EventEmitter { /** * Gets an entry from the multicast table. * @param uint8_t The index of a multicast table entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberMulticastTableEntry * The contents of the multicast entry. */ async ezspGetMulticastTableEntry(index: number): Promise<[SLStatus, value: EmberMulticastTableEntry]> { @@ -6130,7 +6139,7 @@ export class Ezsp extends EventEmitter { * Sets an entry in the multicast table. * @param index uint8_t The index of a multicast table entry * @param EmberMulticastTableEntry * The contents of the multicast entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetMulticastTableEntry(index: number, value: EmberMulticastTableEntry): Promise { this.startCommand(EzspFrameID.SET_MULTICAST_TABLE_ENTRY); @@ -6170,7 +6179,7 @@ export class Ezsp extends EventEmitter { /** * Write the current node Id, PAN ID, or Node type to the tokens * @param erase Erase the node type or not - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspWriteNodeData(erase: boolean): Promise { this.startCommand(EzspFrameID.WRITE_NODE_DATA); @@ -6193,7 +6202,7 @@ export class Ezsp extends EventEmitter { * @param messageContents uint8_t * The raw message. * @param priority uint8_t transmit priority. * @param useCca Should we enable CCA or not. - * @returns An sl_status_t value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSendRawMessage(messageContents: Buffer, priority: EmberTransmitPriority, useCca: boolean): Promise { this.startCommand(EzspFrameID.SEND_RAW_MESSAGE); @@ -6328,7 +6337,9 @@ export class Ezsp extends EventEmitter { * A callback invoked by the EmberZNet stack when the MAC has finished * transmitting a raw message. * @param messageContents (v14+) - * @param status EMBER_SUCCESS if the transmission was successful, or EMBER_DELIVERY_FAILED if not + * @param status + * - SLStatus.OK if the transmission was successful, + * - SLStatus.ZIGBEE_DELIVERY_FAILED if not */ ezspRawTransmitCompleteHandler(messageContents: Buffer, status: SLStatus): void { logger.debug(`ezspRawTransmitCompleteHandler(): callback called with: [messageContents=${messageContents.toString('hex')}], ` @@ -6336,9 +6347,9 @@ export class Ezsp extends EventEmitter { } /** - * This function is useful to sleepy end devices. This function will set the - * retry interval (in milliseconds) for mac data poll. This interval is the time - * in milliseconds the device waits before retrying a data poll when a MAC level + * This function is useful to sleepy end devices. + * This function will set the retry interval (in milliseconds) for mac data poll. + * This interval is the time in milliseconds the device waits before retrying a data poll when a MAC level * data poll fails for any reason. * @param waitBeforeRetryIntervalMs uint32_t Time in milliseconds the device waits before retrying * a data poll when a MAC level data poll fails for any reason. @@ -6379,7 +6390,7 @@ export class Ezsp extends EventEmitter { /** * Sets the priority masks and related variables for choosing the best beacon. * @param param EmberBeaconClassificationParams * The beacon prioritization related variable - * @returns The attempt to set the pramaters returns EMBER_SUCCESS + * @returns The attempt to set the parameters returns SLStatus.OK */ async ezspSetBeaconClassificationParams(param: EmberBeaconClassificationParams): Promise { this.startCommand(EzspFrameID.SET_BEACON_CLASSIFICATION_PARAMS); @@ -6398,7 +6409,7 @@ export class Ezsp extends EventEmitter { /** * Gets the priority masks and related variables for choosing the best beacon. - * @returns The attempt to get the pramaters returns EMBER_SUCCESS + * @returns The attempt to get the parameters returns SLStatus.OK * @returns EmberBeaconClassificationParams * Gets the beacon prioritization related variable */ async ezspGetBeaconClassificationParams(): Promise<[SLStatus, param: EmberBeaconClassificationParams]> { @@ -6538,7 +6549,7 @@ export class Ezsp extends EventEmitter { * Exports a key from security manager based on passed context. * @param context sl_zb_sec_man_context_t * Metadata to identify the requested key. * @returns sl_zb_sec_man_key_t * Data to store the exported key in. - * @returns sl_status_t * The success or failure code of the operation. + * @returns SLStatus * The success or failure code of the operation. */ async ezspExportKey(context: SecManContext): Promise<[SLStatus, key: SecManKey]> { /** @@ -6578,7 +6589,7 @@ export class Ezsp extends EventEmitter { * @note The context->derived_type must be SL_ZB_SEC_MAN_DERIVED_KEY_TYPE_NONE. * Other values are ignored. * - * @return SL_STATUS_OK upon success, a valid error code otherwise. + * @return SLStatus.OK upon success, a valid error code otherwise. */ // NOTE: added for good measure if (context.coreKeyType === SecManKeyType.INTERNAL) { @@ -6651,7 +6662,7 @@ export class Ezsp extends EventEmitter { * @note The context->derived_type must be SL_ZB_SEC_MAN_DERIVED_KEY_TYPE_NONE, * else, an error will be thrown. Key derivations, which are used in crypto * operations, are performed using the ::sl_zb_sec_man_load_key_context routine. - * @return SL_STATUS_OK upon success, a valid error code otherwise. + * @return SLStatus.OK upon success, a valid error code otherwise. */ // NOTE: added for good measure if (context.coreKeyType === SecManKeyType.INTERNAL) { @@ -6692,8 +6703,8 @@ export class Ezsp extends EventEmitter { * @param address The address to search for. Alternatively, all zeros may be passed in to search for the first empty entry. * @param linkKey This indicates whether to search for an entry that contains a link key or a master key. * true means to search for an entry with a Link Key. - * @returns uint8_t This indicates the index of the entry that matches the search - * criteria. A value of 0xFF is returned if not matching entry is found. + * @returns uint8_t This indicates the index of the entry that matches the search criteria. + * A value of 0xFF is returned if not matching entry is found. */ async ezspFindKeyTableEntry(address: EUI64, linkKey: boolean): Promise { this.startCommand(EzspFrameID.FIND_KEY_TABLE_ENTRY); @@ -6717,7 +6728,7 @@ export class Ezsp extends EventEmitter { * short and long address arguments. * @param destinationNodeId The short address of the node to which this command will be sent * @param destinationEui64 The long address of the node to which this command will be sent - * @returns An EmberStatus value indicating success of failure of the operation + * @returns An SLStatus value indicating success of failure of the operation */ async ezspSendTrustCenterLinkKey(destinationNodeId: NodeId, destinationEui64: EUI64): Promise { this.startCommand(EzspFrameID.SEND_TRUST_CENTER_LINK_KEY); @@ -6739,8 +6750,7 @@ export class Ezsp extends EventEmitter { * This function erases the data in the key table entry at the specified index. * If the index is invalid, false is returned. * @param index uint8_t This indicates the index of entry to erase. - * @returns ::EMBER_SUCCESS if the index is valid and the key data was erased. - * ::EMBER_KEY_INVALID if the index is out of range for the size of the key table. + * @returns The success or failure of the operation. */ async ezspEraseKeyTableEntry(index: number): Promise { this.startCommand(EzspFrameID.ERASE_KEY_TABLE_ENTRY); @@ -6759,8 +6769,7 @@ export class Ezsp extends EventEmitter { /** * This function clears the key table of the current network. - * @returns ::EMBER_SUCCESS if the key table was successfully cleared. - * ::EMBER_INVALID_CALL otherwise. + * @returns The success or failure of the operation. */ async ezspClearKeyTable(): Promise { this.startCommand(EzspFrameID.CLEAR_KEY_TABLE); @@ -6793,7 +6802,7 @@ export class Ezsp extends EventEmitter { * returned via ezspZigbeeKeyEstablishmentHandler(...) * @param partner This is the IEEE address of the partner device that will share the link key. * @returns The success or failure of sending the request. - * This is not the final result of the attempt. ezspZigbeeKeyEstablishmentHandler(...) will return that. + * This is not the final result of the attempt. ezspZigbeeKeyEstablishmentHandler(...) will return that. */ async ezspRequestLinkKey(partner: EUI64): Promise { this.startCommand(EzspFrameID.REQUEST_LINK_KEY); @@ -6819,8 +6828,8 @@ export class Ezsp extends EventEmitter { * Request Key, and Verify Key Confirm messages. The number of attempts resets for each message type sent * (e.g., if maxAttempts is 3, up to 3 Node Descriptors are sent, up to 3 Request Keys, and up to 3 Verify Key Confirm messages are sent). * @returns The success or failure of sending the request. - * If the Node Descriptor is successfully transmitted, ezspZigbeeKeyEstablishmentHandler(...) - * will be called at a later time with a final status result. + * If the Node Descriptor is successfully transmitted, ezspZigbeeKeyEstablishmentHandler(...) + * will be called at a later time with a final status result. */ async ezspUpdateTcLinkKey(maxAttempts: number): Promise { this.startCommand(EzspFrameID.UPDATE_TC_LINK_KEY); @@ -6878,7 +6887,7 @@ export class Ezsp extends EventEmitter { * containing information about whether the current and next network keys are set, and the * sequence numbers associated with each key. * - * @return sl_status_t SL_STATUS_OK + * @return SLStatus SLStatus.OK * */ this.startCommand(EzspFrameID.GET_NETWORK_KEY_INFO); @@ -6898,8 +6907,8 @@ export class Ezsp extends EventEmitter { /** * Retrieve metadata about an APS link key. Does not retrieve contents. * @param context sl_zb_sec_man_context_t * Context used to input information about key. + * @returns Status of metadata retrieval operation. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the referenced key. - * @returns sl_status_t * Status of metadata retrieval operation. */ async ezspGetApsKeyInfo(context: SecManContext): Promise<[status: SLStatus, keyData: SecManAPSKeyMetadata]> { /** @@ -6927,10 +6936,10 @@ export class Ezsp extends EventEmitter { * * @returns keyData sl_zb_sec_man_aps_key_metadata_t* [OUT] Metadata to fill in. * - * @returns SL_STATUS_OK if successful, SL_STATUS_NOT_FOUND if + * @returns SLStatus.OK if successful, SLStatus.NOT_FOUND if * the key_index or eui64 does not result in a found entry, - * SL_STATUS_INVALID_TYPE if the core key type is not an APS layer key (e.g. - * SL_ZB_SEC_MAN_KEY_TYPE_NETWORK), or SL_STATUS_INVALID_MODE if core_key_type + * SLStatus.INVALID_TYPE if the core key type is not an APS layer key (e.g. + * SL_ZB_SEC_MAN_KEY_TYPE_NETWORK), or SLStatus.INVALID_MODE if core_key_type * is SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK and the initial security state does not * indicate the a preconfigured key has been set (that is, both * EMBER_HAVE_PRECONFIGURED_KEY and @@ -6980,7 +6989,7 @@ export class Ezsp extends EventEmitter { * @param address EUI64 [IN] The EUI belonging to the key. * @param plaintext_key sl_zb_sec_man_key_t* [IN] A pointer to the key to import. * - * @return SL_STATUS_OK upon success, a valid error code otherwise. + * @return SLStatus.OK upon success, a valid error code otherwise. * */ this.startCommand(EzspFrameID.IMPORT_LINK_KEY); @@ -7002,10 +7011,10 @@ export class Ezsp extends EventEmitter { /** * Export the link key at given index from the key table. * @param uint8_t Index of key to export. + * @returns Status of key export operation. * @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with. * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. - * @returns sl_status_t * Status of key export operation. */ async ezspExportLinkKeyByIndex(index: number) : Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, keyData: SecManAPSKeyMetadata]> { @@ -7049,10 +7058,10 @@ export class Ezsp extends EventEmitter { /** * Export the link key associated with the given EUI from the key table. * @param eui EUI64 associated with the key to export. + * @returns Status of key export operation. * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zigbee_sec_man_context_t * Context referencing the exported key. Contains information like the EUI64 address it is associated with. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. - * @returns sl_status_t * Status of key export operation. */ async ezspExportLinkKeyByEui(eui: EUI64): Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, keyData: SecManAPSKeyMetadata]> { /** @@ -7107,7 +7116,7 @@ export class Ezsp extends EventEmitter { * @param context sl_zb_sec_man_context_t* [IN] The context to check for validity. The fields that must be set depend * on the key type set in the context, as enough information is needed to identify the key. * - * @return sl_status_t SL_STATUS_OK upon success, SL_STATUS_NOT_FOUND otherwise. + * @return SLStatus SLStatus.OK upon success, SLStatus.NOT_FOUND otherwise. */ this.startCommand(EzspFrameID.CHECK_KEY_CONTEXT); this.buffalo.writeSecManContext(context); @@ -7162,12 +7171,10 @@ export class Ezsp extends EventEmitter { /** * Export a transient link key from a given table index. * @param uint8_t Index to export from. + * @returns Status of key export operation. * @returns sl_zb_sec_man_context_t * Context struct for export operation. * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. - * @returns sl_status_t * Status of key export operation. - * - SL_STATUS_OK upon success - * - SL_STATUS_NOT_FOUND otherwise. */ async ezspExportTransientKeyByIndex(index: number) : Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata]> { @@ -7200,12 +7207,10 @@ export class Ezsp extends EventEmitter { /** * Export a transient link key associated with a given EUI64 * @param eui Index to export from. + * @returns Status of key export operation. * @returns sl_zb_sec_man_context_t * Context struct for export operation. * @returns sl_zb_sec_man_key_t * The exported key. * @returns sl_zb_sec_man_aps_key_metadata_t * Metadata about the key. - * @returns sl_status_t * Status of key export operation. - * - SL_STATUS_OK upon success - * - SL_STATUS_NOT_FOUND otherwise. */ async ezspExportTransientKeyByEui(eui: EUI64): Promise<[SLStatus, context: SecManContext, plaintextKey: SecManKey, key_data: SecManAPSKeyMetadata]> { @@ -7321,7 +7326,7 @@ export class Ezsp extends EventEmitter { * send the Switch Key after sending the alternate encryption key. * @param key EmberKeyData * An optional pointer to a 16-byte encryption key (EMBER_ENCRYPTION_KEY_SIZE). * An all zero key may be passed in, which will cause the stack to randomly generate a new key. - * @returns EmberStatus value that indicates the success or failure of the command. + * @returns SLStatus value that indicates the success or failure of the command. */ async ezspBroadcastNextNetworkKey(key: EmberKeyData): Promise { this.startCommand(EzspFrameID.BROADCAST_NEXT_NETWORK_KEY); @@ -7341,8 +7346,7 @@ export class Ezsp extends EventEmitter { /** * This function broadcasts a switch key message to tell all nodes to change to * the sequence number of the previously sent Alternate Encryption Key. - * @returns EmberStatus value that indicates the success or failure of the - * command. + * @returns SLStatus value that indicates the success or failure of the command. */ async ezspBroadcastNetworkKeySwitch(): Promise { this.startCommand(EzspFrameID.BROADCAST_NETWORK_KEY_SWITCH); @@ -7396,7 +7400,7 @@ export class Ezsp extends EventEmitter { * @param destShort The node ID of the device that will receive the message * @param destLong The long address (EUI64) of the device that will receive the message. * @param targetLong The long address (EUI64) of the device to be removed. - * @returns An EmberStatus value indicating success, or the reason for failure + * @returns An SLStatus value indicating success, or the reason for failure */ async ezspRemoveDevice(destShort: NodeId, destLong: EUI64, targetLong: EUI64): Promise { this.startCommand(EzspFrameID.REMOVE_DEVICE); @@ -7422,7 +7426,7 @@ export class Ezsp extends EventEmitter { * @param destShort The node ID of the device that will receive the message * @param destLong The long address (EUI64) of the device that will receive the message. * @param key EmberKeyData * The NWK key to send to the new device. - * @returns An EmberStatus value indicating success, or the reason for failure + * @returns An SLStatus value indicating success, or the reason for failure */ async ezspUnicastNwkKeyUpdate(destShort: NodeId, destLong: EUI64, key: EmberKeyData): Promise { this.startCommand(EzspFrameID.UNICAST_NWK_KEY_UPDATE); @@ -7684,56 +7688,6 @@ export class Ezsp extends EventEmitter { return [status, localCert]; } - /** - * LEGACY FUNCTION: This functionality has been replaced by a single bit in the - * EmberApsFrame, EMBER_APS_OPTION_DSA_SIGN. Devices wishing to send signed - * messages should use that as it requires fewer function calls and message - * buffering. The dsaSignHandler response is still called when - * EMBER_APS_OPTION_DSA_SIGN is used. However, this function is still supported. - * This function begins the process of signing the passed message contained - * within the messageContents array. If no other ECC operation is going on, it - * will immediately return with EMBER_OPERATION_IN_PROGRESS to indicate the - * start of ECC operation. It will delay a period of time to let APS retries - * take place, but then it will shut down the radio and consume the CPU - * processing until the signing is complete. This may take up to 1 second. The - * signed message will be returned in the dsaSignHandler response. Note that the - * last byte of the messageContents passed to this function has special - * significance. As the typical use case for DSA signing is to sign the ZCL - * payload of a DRLC Report Event Status message in SE 1.0, there is often both - * a signed portion (ZCL payload) and an unsigned portion (ZCL header). The last - * byte in the content of messageToSign is therefore used as a special indicator - * to signify how many bytes of leading data in the array should be excluded - * from consideration during the signing process. If the signature needs to - * cover the entire array (all bytes except last one), the caller should ensure - * that the last byte of messageContents is 0x00. When the signature operation - * is complete, this final byte will be replaced by the signature type indicator - * (0x01 for ECDSA signatures), and the actual signature will be appended to the - * original contents after this byte. - * @param messageLength uint8_t The length of the messageContents parameter in bytes. - * @param messageContents uint8_t * The message contents for which to create a signature. - * Per above notes, this may include a leading portion of data not included in the signature, - * in which case the last byte of this array should be set to the index of the first byte - * to be considered for signing. Otherwise, the last byte of messageContents should be 0x00 - * to indicate that a signature should occur across the entire contents. - * @returns EMBER_OPERATION_IN_PROGRESS if the stack has queued up the operation - * for execution. EMBER_INVALID_CALL if the operation can't be performed in this - * context, possibly because another ECC operation is pending. - */ - async ezspDsaSign(messageContents: Buffer): Promise { - this.startCommand(EzspFrameID.DSA_SIGN); - this.buffalo.writePayload(messageContents); - - const sendStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new EzspError(sendStatus); - } - - const status = this.buffalo.readStatus(this.version); - - return status; - } - /** * Callback * The handler that returns the results of the signing operation. On success, @@ -7873,7 +7827,7 @@ export class Ezsp extends EventEmitter { * be passed up with a CRC failure. All other mfglib functions will return an * error until the mfglibStart() has been called * @param rxCallback true to generate a mfglibRxHandler callback when a packet is received. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalStart(rxCallback: boolean): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START); @@ -7894,7 +7848,7 @@ export class Ezsp extends EventEmitter { * Deactivate use of mfglib test routines; restores the hardware to the state it * was in prior to mfglibStart() and stops receiving packets started by * mfglibStart() at the same time. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalEnd(): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_END); @@ -7916,7 +7870,7 @@ export class Ezsp extends EventEmitter { * transmitting tone, application must call mfglibStopTone(), allowing it the * flexibility to determine its own criteria for tone duration (time, event, * etc.) - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalStartTone(): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START_TONE); @@ -7934,7 +7888,7 @@ export class Ezsp extends EventEmitter { /** * Stops transmitting tone started by mfglibStartTone(). - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalStopTone(): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_STOP_TONE); @@ -7953,7 +7907,7 @@ export class Ezsp extends EventEmitter { /** * Starts transmitting a random stream of characters. This is so that the radio * modulation can be measured. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalStartStream(): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_START_STREAM); @@ -7972,7 +7926,7 @@ export class Ezsp extends EventEmitter { /** * Stops transmitting a random stream of characters started by * mfglibStartStream(). - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalStopStream(): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_STOP_STREAM); @@ -7995,7 +7949,7 @@ export class Ezsp extends EventEmitter { * last two bytes of packetContents[] with the 16-bit CRC for the packet. * @param packetLength uint8_t The length of the packetContents parameter in bytes. Must be greater than 3 and less than 123. * @param packetContents uint8_t * The packet to send. The last two bytes will be replaced with the 16-bit CRC. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalSendPacket(packetContents: Buffer): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SEND_PACKET); @@ -8016,7 +7970,7 @@ export class Ezsp extends EventEmitter { * Sets the radio channel. Calibration occurs if this is the first time the * channel has been used. * @param channel uint8_t The channel to switch to. Valid values are 11 - 26. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalSetChannel(channel: number): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SET_CHANNEL); @@ -8059,7 +8013,7 @@ export class Ezsp extends EventEmitter { * available to the caller via mfglibGetPower(). * @param txPowerMode uint16_t Power mode. Refer to txPowerModes in stack/include/ember-types.h for possible values. * @param power int8_t Power in units of dBm. Refer to radio data sheet for valid range. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async mfglibInternalSetPower(txPowerMode: EmberTXPowerMode, power: number): Promise { this.startCommand(EzspFrameID.MFGLIB_INTERNAL_SET_POWER); @@ -8121,7 +8075,7 @@ export class Ezsp extends EventEmitter { * channel with current power settings. STANDALONE_BOOTLOADER_RECOVERY_MODE: Will listen for an over-the-air image * transfer on the default channel with default power settings. Both modes also allow an image transfer to begin * with XMODEM over the serial protocol's Bootloader Frame. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspLaunchStandaloneBootloader(mode: number): Promise { this.startCommand(EzspFrameID.LAUNCH_STANDALONE_BOOTLOADER); @@ -8146,7 +8100,7 @@ export class Ezsp extends EventEmitter { * @param destEui64 The EUI64 of the target node. Ignored if the broadcast field is set to true. * @param messageLength uint8_t The length of the messageContents parameter in bytes. * @param messageContents uint8_t * The multicast message. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSendBootloadMessage(broadcast: boolean, destEui64: EUI64, messageContents: Buffer): Promise { this.startCommand(EzspFrameID.SEND_BOOTLOAD_MESSAGE); @@ -8211,8 +8165,8 @@ export class Ezsp extends EventEmitter { * Callback * A callback invoked by the EmberZNet stack when the MAC has finished * transmitting a bootload message. - * @param status An EmberStatus value of SL_STATUS_OK if an ACK was received from the destination - * or SL_STATUS_ZIGBEE_DELIVERY_FAILED if no ACK was received. + * @param status An EmberStatus value of SLStatus.OK if an ACK was received from the destination + * or SLStatus.ZIGBEE_DELIVERY_FAILED if no ACK was received. * @param messageLength uint8_t The length of the messageContents parameter in bytes. * @param messageContents uint8_t * The message that was sent. */ @@ -8265,7 +8219,7 @@ export class Ezsp extends EventEmitter { * @param beginConfiguration Determines the new mode of operation. * true causes the node to enter manufacturing configuration. * false causes the node to return to normal network operation. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSetPacketMode(beginConfiguration: boolean): Promise { if (this.version < 0x0E) { @@ -8292,7 +8246,7 @@ export class Ezsp extends EventEmitter { * to place the DUT into normal network operation for testing. * This function executes only during manufacturing configuration mode and returns an error otherwise. * If successful, the DUT acknowledges the reboot command within 20 milliseconds and then reboots. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSendRebootCommand(): Promise { if (this.version < 0x0E) { @@ -8317,7 +8271,7 @@ export class Ezsp extends EventEmitter { * This function executes only during manufacturing configuration mode and returns an error otherwise. * If successful, the DUT acknowledges the new EUI ID within 150 milliseconds. * @param newId The 8-byte EUID for the DUT. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSendEui64(newId: EUI64): Promise { if (this.version < 0x0E) { @@ -8343,7 +8297,7 @@ export class Ezsp extends EventEmitter { * This function executes only during manufacturing configuration mode and will return an error otherwise. * If successful, the DUT will acknowledge the new string within 150 milliseconds. * @param newString The 16-byte manufacturing string. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSendManufacturingString(newString: number[]): Promise { if (this.version < 0x0E) { @@ -8370,7 +8324,7 @@ export class Ezsp extends EventEmitter { * If successful, the DUT acknowledges the new parameters within 25 milliseconds. * @param supportedBands Sets the radio band for the DUT. See ember-common.h for possible values. * @param crystalOffset Sets the CC1020 crystal offset. This parameter has no effect on the EM2420, and it may safely be set to 0 for this RFIC. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSendRadioParameters(supportedBands: number, crystalOffset: number): Promise { if (this.version < 0x0E) { @@ -8397,7 +8351,7 @@ export class Ezsp extends EventEmitter { * Most implementations will not need to call this function directly. See mfg-test.c for more detail. * This function executes only during manufacturing configuration mode and returns an error otherwise. * @param command A pointer to the outgoing command string. - * @returns An sl_status_t value indicating success or failure of the command. + * @returns An SLStatus value indicating success or failure of the command. */ async ezspMfgTestSendCommand(command: number): Promise { if (this.version < 0x0E) { @@ -8428,7 +8382,7 @@ export class Ezsp extends EventEmitter { * @param networkInfo EmberZllNetwork * Information about the network. * @param op Operation indicator. * @param radioTxPower int8_t Radio transmission power. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspZllNetworkOps(networkInfo: EmberZllNetwork, op: EzspZllNetworkOperation, radioTxPower: number): Promise { this.startCommand(EzspFrameID.ZLL_NETWORK_OPS); @@ -8452,7 +8406,7 @@ export class Ezsp extends EventEmitter { * network. It must be called prior to forming, starting, or joining a network. * @param networkKey EmberKeyData * ZLL Network key. * @param securityState EmberZllInitialSecurityState * Initial security state of the network. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspZllSetInitialSecurityState(networkKey: EmberKeyData, securityState: EmberZllInitialSecurityState): Promise { this.startCommand(EzspFrameID.ZLL_SET_INITIAL_SECURITY_STATE); @@ -8475,7 +8429,7 @@ export class Ezsp extends EventEmitter { * emberZllSetInitialSecurityState, this can be called while a network is * already established. * @param securityState EmberZllInitialSecurityState * Security state of the network. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspZllSetSecurityStateWithoutKey(securityState: EmberZllInitialSecurityState): Promise { this.startCommand(EzspFrameID.ZLL_SET_SECURITY_STATE_WITHOUT_KEY); @@ -8497,7 +8451,7 @@ export class Ezsp extends EventEmitter { * @param channelMask uint32_t The range of channels to scan. * @param radioPowerForScan int8_t The radio output power used for the scan requests. * @param nodeType The node type of the local device. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspZllStartScan(channelMask: number, radioPowerForScan: number, nodeType: EmberNodeType): Promise { this.startCommand(EzspFrameID.ZLL_START_SCAN); @@ -8520,7 +8474,7 @@ export class Ezsp extends EventEmitter { * This call will change the mode of the radio so that the receiver is on for a * specified amount of time when the device is idle. * @param durationMs uint32_t The duration in milliseconds to leave the radio on. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspZllSetRxOnWhenIdle(durationMs: number): Promise { this.startCommand(EzspFrameID.ZLL_SET_RX_ON_WHEN_IDLE); @@ -8892,7 +8846,7 @@ export class Ezsp extends EventEmitter { * @param gpdAsdu uint8_t * The GP command payload. * @param gpepHandle uint8_t The handle to refer to the GPDF. * @param gpTxQueueEntryLifetimeMs uint16_t How long to keep the GPDF in the TX Queue. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspDGpSend(action: boolean, useCca: boolean, addr: EmberGpAddress, gpdCommandId: number, gpdAsdu: Buffer, gpepHandle: number, gpTxQueueEntryLifetimeMs: number): Promise { @@ -8920,7 +8874,7 @@ export class Ezsp extends EventEmitter { * Callback * A callback to the GP endpoint to indicate the result of the GPDF * transmission. - * @param status An EmberStatus value indicating success or the reason for failure. + * @param status An SLStatus value indicating success or the reason for failure. * @param gpepHandle uint8_t The handle of the GPDF. */ ezspDGpSentHandler(status: SLStatus, gpepHandle: number): void { @@ -8987,7 +8941,7 @@ export class Ezsp extends EventEmitter { /** * Retrieves the proxy table entry stored at the passed index. * @param proxyIndex uint8_t The index of the requested proxy table entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberGpProxyTableEntry * An EmberGpProxyTableEntry struct containing a copy of the requested proxy entry. */ async ezspGpProxyTableGetEntry(proxyIndex: number): Promise<[SLStatus, entry: EmberGpProxyTableEntry]> { @@ -9029,7 +8983,7 @@ export class Ezsp extends EventEmitter { /** * Retrieves the sink table entry stored at the passed index. * @param sinkIndex uint8_t The index of the requested sink table entry. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the requested sink entry. */ async ezspGpSinkTableGetEntry(sinkIndex: number): Promise<[SLStatus, entry: EmberGpSinkTableEntry]> { @@ -9072,7 +9026,7 @@ export class Ezsp extends EventEmitter { * Retrieves the sink table entry stored at the passed index. * @param sinkIndex uint8_t The index of the requested sink table entry. * @param entry EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the sink entry to be updated. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspGpSinkTableSetEntry(sinkIndex: number, entry: EmberGpSinkTableEntry): Promise { this.startCommand(EzspFrameID.GP_SINK_TABLE_SET_ENTRY); @@ -9174,7 +9128,7 @@ export class Ezsp extends EventEmitter { * @param uint16_t gpm address for security. * @param uint16_t gpm address for pairing. * @param uint8_t sink endpoint. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspGpSinkCommission(options: number, gpmAddrForSecurity: number, gpmAddrForPairing: number, sinkEndpoint: number): Promise { this.startCommand(EzspFrameID.GP_SINK_COMMISSION); @@ -9250,7 +9204,7 @@ export class Ezsp extends EventEmitter { /** * Gets the token information for a single token at provided index * @param index uint8_t Index of the token in the token table for which information is needed. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberTokenInfo * Token information. */ async ezspGetTokenInfo(index: number): Promise<[SLStatus, tokenInfo: EmberTokenInfo]> { @@ -9273,7 +9227,7 @@ export class Ezsp extends EventEmitter { * Gets the token data for a single token with provided key * @param token uint32_t Key of the token in the token table for which data is needed. * @param index uint32_t Index in case of the indexed token. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. * @returns EmberTokenData * Token Data */ async ezspGetTokenData(token: number, index: number): Promise<[SLStatus, tokenData: EmberTokenData]> { @@ -9298,7 +9252,7 @@ export class Ezsp extends EventEmitter { * @param token uint32_t Key of the token in the token table for which data is to be set. * @param index uint32_t Index in case of the indexed token. * @param EmberTokenData * Token Data - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspSetTokenData(token: number, index: number, tokenData: EmberTokenData): Promise { this.startCommand(EzspFrameID.SET_TOKEN_DATA); @@ -9331,7 +9285,7 @@ export class Ezsp extends EventEmitter { /** * Run GP security test vectors. - * @returns An EmberStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. */ async ezspGpSecurityTestVectors(): Promise { this.startCommand(EzspFrameID.GP_SECURITY_TEST_VECTORS); diff --git a/test/adapter/ember/ezspBuffalo.test.ts b/test/adapter/ember/ezspBuffalo.test.ts index 29cd22abf9..c1fbd8bb8e 100644 --- a/test/adapter/ember/ezspBuffalo.test.ts +++ b/test/adapter/ember/ezspBuffalo.test.ts @@ -1,3 +1,4 @@ +import {SLStatus} from '../../../src/adapter/ember/enums'; import {EzspBuffalo} from '../../../src/adapter/ember/ezsp/buffalo'; import {EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, EZSP_FRAME_CONTROL_COMMAND, EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK, EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET, EZSP_FRAME_CONTROL_SLEEP_MODE_MASK, EZSP_FRAME_ID_INDEX, EZSP_MAX_FRAME_LENGTH, EZSP_PARAMETERS_INDEX, EZSP_SEQUENCE_INDEX} from '../../../src/adapter/ember/ezsp/consts'; import {EzspFrameID} from '../../../src/adapter/ember/ezsp/enums'; @@ -45,4 +46,39 @@ describe('Ember EZSP Buffalo', () => { | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) ); }); + + it('Maps EmberStatus/EzspStatus to SLStatus', () => { + buffalo.setCommandByte(0, 0x00); + buffalo.setCommandByte(1, 0x00); + buffalo.setCommandByte(2, 0x00); + buffalo.setCommandByte(3, 0x00); + // zero always zero + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D)).toStrictEqual(SLStatus.OK); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D, false)).toStrictEqual(SLStatus.OK); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0E)).toStrictEqual(SLStatus.OK); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0E, false)).toStrictEqual(SLStatus.OK); + + buffalo.setCommandByte(0, 0x02); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D)).toStrictEqual(SLStatus.INVALID_PARAMETER); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D, false)).toStrictEqual(SLStatus.ZIGBEE_EZSP_ERROR); + + buffalo.setCommandByte(0, 0x9C); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D)).toStrictEqual(SLStatus.ZIGBEE_NETWORK_OPENED); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D, false)).toStrictEqual(SLStatus.ZIGBEE_EZSP_ERROR); + + // no mapped value + buffalo.setCommandByte(0, 0x4B); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D)).toStrictEqual(SLStatus.BUS_ERROR); + buffalo.setPosition(0); + expect(buffalo.readStatus(0x0D, false)).toStrictEqual(SLStatus.ZIGBEE_EZSP_ERROR); + }); }); From 46e6018a7dde3078efe18e2c8d6f0e622863c6bd Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 18:51:51 +0200 Subject: [PATCH 06/17] Cleanup. --- src/adapter/ember/ezsp/enums.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ember/ezsp/enums.ts b/src/adapter/ember/ezsp/enums.ts index 669b3efcd3..e7e789e03a 100644 --- a/src/adapter/ember/ezsp/enums.ts +++ b/src/adapter/ember/ezsp/enums.ts @@ -312,7 +312,7 @@ export enum EzspFrameID { CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1 = 0x00EE, GET_CERTIFICATE = 0x00A5, GET_CERTIFICATE283K1 = 0x00EC, - DSA_SIGN = 0x00A6, + // DSA_SIGN = 0x00A6,// use EmberApsOption.DSA_SIGN instead DSA_SIGN_HANDLER = 0x00A7, DSA_VERIFY = 0x00A3, DSA_VERIFY_HANDLER = 0x0078, From c2d63be119537ba329b72ad9dd9dbd35d3a21689 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sat, 22 Jun 2024 20:58:22 +0200 Subject: [PATCH 07/17] Set new protocol version. --- src/adapter/ember/ezsp/consts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ember/ezsp/consts.ts b/src/adapter/ember/ezsp/consts.ts index bcf79d62c6..92510cee36 100644 --- a/src/adapter/ember/ezsp/consts.ts +++ b/src/adapter/ember/ezsp/consts.ts @@ -3,7 +3,7 @@ export const EZSP_MIN_PROTOCOL_VERSION = 0x0D; /** Latest EZSP protocol version */ -export const EZSP_PROTOCOL_VERSION = 0x0D; +export const EZSP_PROTOCOL_VERSION = 0x0E; /** EZSP max length + Frame Control extra byte + Frame ID extra byte */ export const EZSP_MAX_FRAME_LENGTH = (200 + 1 + 1); From f06bae5d4939b6d87e6c861c38fa223fc1967aea Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 02:02:33 +0200 Subject: [PATCH 08/17] Remove `messageContents` from `ezspMessageSentHandler` --- src/adapter/ember/adapter/emberAdapter.ts | 6 ++++-- src/adapter/ember/ezsp/ezsp.ts | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index c317c56d2c..13f52f99aa 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -941,8 +941,8 @@ export class EmberAdapter extends Adapter { EzspPolicyId.BINDING_MODIFICATION_POLICY, EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS ); - // return message tag and message contents in ezspMessageSentHandler() - await this.emberSetEzspPolicy(EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK); + // return message tag only in ezspMessageSentHandler() + await this.emberSetEzspPolicy(EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK); await this.emberSetEzspValue(EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE, 2, lowHighBytes(MAXIMUM_APS_PAYLOAD_LENGTH)); await this.emberSetEzspValue(EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE, 2, lowHighBytes(MAXIMUM_APS_PAYLOAD_LENGTH)); @@ -984,6 +984,8 @@ export class EmberAdapter extends Adapter { await this.emberSetEzspConfigValue(EzspConfigId.BROADCAST_TABLE_SIZE, this.stackConfig.BROADCAST_TABLE_SIZE); await this.emberSetEzspConfigValue(EzspConfigId.NEIGHBOR_TABLE_SIZE, this.stackConfig.NEIGHBOR_TABLE_SIZE); await this.emberSetEzspConfigValue(EzspConfigId.END_DEVICE_POLL_TIMEOUT, this.stackConfig.END_DEVICE_POLL_TIMEOUT); + // SL_ZIGBEE_EZSP_CONFIG_ZLL_GROUP_ADDRESSES 1 + // SL_ZIGBEE_EZSP_CONFIG_ZLL_RSSI_THRESHOLD -128 await this.emberSetEzspConfigValue(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, this.stackConfig.TRANSIENT_KEY_TIMEOUT_S); await this.emberSetEzspConfigValue(EzspConfigId.RETRY_QUEUE_SIZE, this.stackConfig.RETRY_QUEUE_SIZE); await this.emberSetEzspConfigValue(EzspConfigId.SOURCE_ROUTE_TABLE_SIZE, this.stackConfig.SOURCE_ROUTE_TABLE_SIZE); diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 2687ed1c4b..82c92ce3ea 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -802,18 +802,20 @@ export class Ezsp extends EventEmitter { const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); const messageTag = this.buffalo.readUInt8(); const status = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); + // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely + // const messageContents = this.buffalo.readPayload(); - this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag, messageContents); + this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag); } else { const status = this.buffalo.readUInt32(); const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); const indexOrDestination = this.buffalo.readUInt16(); const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); const messageTag = this.buffalo.readUInt16(); - const messageContents = this.buffalo.readPayload(); + // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely + // const messageContents = this.buffalo.readPayload(); - this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag, messageContents); + this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag); } break; } @@ -850,7 +852,7 @@ export class Ezsp extends EventEmitter { this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); } else { const type: EmberIncomingMessageType = this.buffalo.readUInt8(); - const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); + const apsFrame = this.buffalo.readEmberApsFrame(); const packetInfo = this.buffalo.readEmberRxPacketInfo(); const messageContents = this.buffalo.readPayload(); this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); @@ -4947,11 +4949,11 @@ export class Ezsp extends EventEmitter { * for the messageContentsInCallback policy is messageTagAndContentsInCallback. */ ezspMessageSentHandler(status: SLStatus, type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number, - messageContents: Buffer): void { + messageContents: Buffer = undefined): void { logger.debug( `ezspMessageSentHandler(): callback called with: [status=${SLStatus[status]}], [type=${EmberOutgoingMessageType[type]}], ` - + `[indexOrDestination=${indexOrDestination}], [apsFrame=${JSON.stringify(apsFrame)}], [messageTag=${messageTag}], ` - + `[messageContents=${messageContents.toString('hex')}]`, + + `[indexOrDestination=${indexOrDestination}], [apsFrame=${JSON.stringify(apsFrame)}], [messageTag=${messageTag}]` + + (messageContents ? `, [messageContents=${messageContents.toString('hex')}]` : ''), NS, ); From cc8cbfcae49c3c7c2bc9ca7a04b58e46e2f4ff8c Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:25:51 +0200 Subject: [PATCH 09/17] Fix comment. --- src/adapter/ember/ezsp/ezsp.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 82c92ce3ea..315a6ba919 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -4938,8 +4938,9 @@ export class Ezsp extends EventEmitter { /** * Callback * A callback indicating the stack has completed sending a message. - * @param status An EmberStatus value of EMBER_SUCCESS if an ACK was received from the destination - * or EMBER_DELIVERY_FAILED if no ACK was received. + * @param status + * - SL_STATUS_OK if an ACK was received from the destination + * - SL_STATUS_ZIGBEE_DELIVERY_FAILED if no ACK was received. * @param type The type of message sent. * @param indexOrDestination uint16_t The destination to which the message was sent, for direct unicasts, * or the address table or binding index for other unicasts. The value is unspecified for multicasts and broadcasts. From afc142769ca5aed3abc96c9795c0bf3392216fc0 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:37:30 +0200 Subject: [PATCH 10/17] Lower log level for failed config. --- src/adapter/ember/adapter/emberAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 13f52f99aa..42b746baa5 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -1742,7 +1742,7 @@ export class EmberAdapter extends Adapter { logger.debug(`[EzspConfigId] SET "${EzspConfigId[configId]}" TO "${value}" with status=${SLStatus[status]}.`, NS); if (status !== SLStatus.OK) { - logger.warning( + logger.info( `[EzspConfigId] Failed to SET "${EzspConfigId[configId]}" TO "${value}" with status=${SLStatus[status]}. ` + `Firmware value will be used instead.`, NS, From e3586aa8cbe01d0fe961f056b42c1dc1d9fbc0b0 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:50:54 +0200 Subject: [PATCH 11/17] Remove deprecated `ezspSetSourceRoute` --- src/adapter/ember/ezsp/enums.ts | 2 +- src/adapter/ember/ezsp/ezsp.ts | 25 ------------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/adapter/ember/ezsp/enums.ts b/src/adapter/ember/ezsp/enums.ts index e7e789e03a..fe813051d4 100644 --- a/src/adapter/ember/ezsp/enums.ts +++ b/src/adapter/ember/ezsp/enums.ts @@ -226,7 +226,7 @@ export enum EzspFrameID { INCOMING_ROUTE_ERROR_HANDLER = 0x0080, INCOMING_NETWORK_STATUS_HANDLER = 0x00C4, INCOMING_ROUTE_RECORD_HANDLER = 0x0059, - SET_SOURCE_ROUTE = 0x00AE, + // SET_SOURCE_ROUTE = 0x00AE,// v9-, no-op since UNICAST_CURRENT_NETWORK_KEY = 0x0050, ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x005B, /** v14+ */ diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 315a6ba919..49d0fdfe46 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -5819,31 +5819,6 @@ export class Ezsp extends EventEmitter { // XXX: could at least trigger a `Events.lastSeenChanged` but this is not currently being listened to at the adapter level } - /** - * Supply a source route for the next outgoing message. - * @param destination The destination of the source route. - * @param relayList uint16_t * The source route. - * @returns - * - SLStatus.OK if the source route was successfully stored, - * - SLStatus.ALLOCATION_FAILED otherwise. - */ - async ezspSetSourceRoute(destination: NodeId, relayList: number[]): Promise { - this.startCommand(EzspFrameID.SET_SOURCE_ROUTE); - this.buffalo.writeUInt16(destination); - this.buffalo.writeUInt8(relayList.length); - this.buffalo.writeListUInt16(relayList); - - const sendStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new EzspError(sendStatus); - } - - const status = this.buffalo.readStatus(this.version); - - return status; - } - /** * Send the network key to a destination. * @param targetShort The destination node of the key. From bd1f0e4e43708bfbb82c85116ec5e5bfc2921edf Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 17:22:02 +0200 Subject: [PATCH 12/17] Move to Zdo spec for response parsing. --- src/adapter/ember/adapter/emberAdapter.ts | 266 +++--- src/adapter/ember/adapter/oneWaitress.ts | 19 +- src/adapter/ember/ezsp/ezsp.ts | 539 +---------- src/adapter/ember/utils/math.ts | 21 - src/adapter/ember/zdo.ts | 1039 --------------------- 5 files changed, 147 insertions(+), 1737 deletions(-) delete mode 100644 src/adapter/ember/zdo.ts diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 42b746baa5..f5439d59e5 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -4,36 +4,37 @@ import {existsSync, readFileSync, renameSync} from 'fs'; import path from 'path'; import SerialPortUtils from '../../serialPortUtils'; import SocketPortUtils from '../../socketPortUtils'; -import {BackupUtils, RealpathSync, Wait} from "../../../utils"; -import {Adapter, TsType} from "../.."; -import {Backup, UnifiedBackupStorage} from "../../../models"; -import * as Zcl from "../../../zspec/zcl"; -import {BroadcastAddress} from '../../../zspec/enums'; -import * as ZSpec from '../../../zspec/consts'; +import {BackupUtils, RealpathSync, Wait} from '../../../utils'; +import {Adapter, TsType} from '../..'; +import {Backup, UnifiedBackupStorage} from '../../../models'; +import * as ZSpec from '../../../zspec'; +import * as Zcl from '../../../zspec/zcl'; +import * as Zdo from '../../../zspec/zdo'; +import * as ZdoTypes from '../../../zspec/zdo/definition/tstypes'; import { DeviceAnnouncePayload, DeviceJoinedPayload, DeviceLeavePayload, Events, ZclPayload -} from "../../events"; -import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from "../utils/math"; -import {Ezsp, EzspEvents} from "../ezsp/ezsp"; +} from '../../events'; +import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from '../utils/math'; +import {Ezsp, EzspEvents} from '../ezsp/ezsp'; import { EMBER_ENCRYPTION_KEY_SIZE, EZSP_MAX_FRAME_LENGTH, EZSP_MIN_PROTOCOL_VERSION, EZSP_PROTOCOL_VERSION, EZSP_STACK_TYPE_MESH -} from "../ezsp/consts"; +} from '../ezsp/consts'; import { EzspConfigId, EzspDecisionBitmask, EzspDecisionId, EzspPolicyId, EzspValueId -} from "../ezsp/enums"; -import {EzspBuffalo} from "../ezsp/buffalo"; +} from '../ezsp/enums'; +import {EzspBuffalo} from '../ezsp/buffalo'; import { EmberApsOption, EmberOutgoingMessageType, @@ -57,7 +58,7 @@ import { EzspNetworkScanType, EmberIncomingMessageType, EmberTransmitPriority, -} from "../enums"; +} from '../enums'; import { EmberAesMmoHashContext, EmberApsFrame, @@ -71,45 +72,7 @@ import { SecManAPSKeyMetadata, SecManContext, SecManKey, -} from "../types"; -import { - EmberZdoStatus, - EndDeviceAnnouncePayload, - LQITableResponsePayload, - SimpleDescriptorResponsePayload, - NodeDescriptorResponsePayload, - ActiveEndpointsResponsePayload, - RoutingTableResponsePayload, - ACTIVE_ENDPOINTS_REQUEST, - BINDING_TABLE_REQUEST, - BIND_REQUEST, - IEEE_ADDRESS_REQUEST, - LEAVE_REQUEST, - LQI_TABLE_REQUEST, - MATCH_DESCRIPTORS_REQUEST, - MULTICAST_BINDING, - NETWORK_ADDRESS_REQUEST, - NODE_DESCRIPTOR_REQUEST, - PERMIT_JOINING_REQUEST, - POWER_DESCRIPTOR_REQUEST, - ROUTING_TABLE_REQUEST, - SIMPLE_DESCRIPTOR_REQUEST, - UNBIND_REQUEST, - UNICAST_BINDING, - ZDO_ENDPOINT, - ZDO_MESSAGE_OVERHEAD, - ZDO_PROFILE_ID, - PERMIT_JOINING_RESPONSE, - NODE_DESCRIPTOR_RESPONSE, - LQI_TABLE_RESPONSE, - ROUTING_TABLE_RESPONSE, - ACTIVE_ENDPOINTS_RESPONSE, - SIMPLE_DESCRIPTOR_RESPONSE, - BIND_RESPONSE, - UNBIND_RESPONSE, - LEAVE_RESPONSE, - NWK_UPDATE_REQUEST -} from "../zdo"; +} from '../types'; import { EMBER_INSTALL_CODE_CRC_SIZE, EMBER_INSTALL_CODE_SIZES, @@ -131,16 +94,16 @@ import { EMBER_ALL_802_15_4_CHANNELS_MASK, ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY, EMBER_MIN_BROADCAST_ADDRESS, -} from "../consts"; -import {EmberRequestQueue} from "./requestQueue"; -import {FIXED_ENDPOINTS} from "./endpoints"; -import {aesMmoHashInit, initNetworkCache, initSecurityManagerContext} from "../utils/initters"; -import {randomBytes} from "crypto"; -import {EmberOneWaitress, OneWaitressEvents} from "./oneWaitress"; -import {logger} from "../../../utils/logger"; +} from '../consts'; +import {EmberRequestQueue} from './requestQueue'; +import {FIXED_ENDPOINTS} from './endpoints'; +import {aesMmoHashInit, initNetworkCache, initSecurityManagerContext} from '../utils/initters'; +import {randomBytes} from 'crypto'; +import {EmberOneWaitress, OneWaitressEvents} from './oneWaitress'; +import {logger} from '../../../utils/logger'; import {EUI64, ExtendedPanId, NodeId, PanId} from '../../../zspec/tstypes'; import {EzspError} from '../ezspError'; -// import {EmberTokensManager} from "./tokensManager"; +// import {EmberTokensManager} from './tokensManager'; const NS = 'zh:ember'; @@ -206,8 +169,8 @@ const autoDetectDefinitions = [ * messages with non-conflicting sequence numbers. */ const APPLICATION_ZDO_SEQUENCE_MASK = 0x7F; -/** Current revision of the spec by zigbee alliance. XXX: what are `Zigbee Pro 2023` devices reporting?? */ -const CURRENT_ZIGBEE_SPEC_REVISION = 23; +/** Current revision of the spec by zigbee alliance supported by Z2M. */ +const CURRENT_ZIGBEE_SPEC_REVISION = 22; /** Each scan period is 15.36ms. Scan for at least 200ms (2^4 + 1 periods) to pick up WiFi beacon frames. */ const ENERGY_SCAN_DURATION = 4; /** Oldest supported EZSP version for backups. Don't take the risk to restore a broken network until older backup versions can be investigated. */ @@ -644,11 +607,10 @@ export class EmberAdapter extends Adapter { * * @param clusterId The ZDO response cluster ID. * @param sender The sender of the response. Should match `payload.nodeId` in many responses. - * @param payload If null, the response indicated a failure. + * @param payload If ZdoStatusError, the response indicated a failure. */ - private async onZDOResponse(status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: unknown) - : Promise { - this.oneWaitress.resolveZDO(status, sender, apsFrame, payload); + private async onZDOResponse(sender: NodeId, apsFrame: EmberApsFrame, payload: unknown | Zdo.StatusError): Promise { + this.oneWaitress.resolveZDO(sender, apsFrame, payload); } /** @@ -659,13 +621,13 @@ export class EmberAdapter extends Adapter { * @param eui64 * @param macCapFlags */ - private async onEndDeviceAnnounce(sender: NodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload): Promise { + private async onEndDeviceAnnounce(sender: NodeId, apsFrame: EmberApsFrame, payload: ZdoTypes.EndDeviceAnnounce): Promise { // reduced function device // if ((payload.capabilities.deviceType === 0)) { // } - this.emit(Events.deviceAnnounce, {networkAddress: payload.nodeId, ieeeAddr: payload.eui64} as DeviceAnnouncePayload); + this.emit(Events.deviceAnnounce, {networkAddress: payload.nwkAddress, ieeeAddr: payload.eui64} as DeviceAnnouncePayload); } /** @@ -1886,7 +1848,7 @@ export class EmberAdapter extends Adapter { if (broadcastMgmtPermitJoin) { // `authentication`: TC significance always 1 (zb specs) - [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(BroadcastAddress.DEFAULT, duration, 1, DEFAULT_APS_OPTIONS)); + [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(ZSpec.BroadcastAddress.DEFAULT, duration, 1, DEFAULT_APS_OPTIONS)); } return [status, apsFrame, messageTag]; @@ -1971,18 +1933,18 @@ export class EmberAdapter extends Adapter { this.zdoRequestBuffalo.setCommandByte(0, messageTag); const apsFrame: EmberApsFrame = { - profileId: ZDO_PROFILE_ID, - clusterId: clusterId, - sourceEndpoint: ZDO_ENDPOINT, - destinationEndpoint: ZDO_ENDPOINT, - options: options, + profileId: Zdo.ZDO_PROFILE_ID, + clusterId, + sourceEndpoint: Zdo.ZDO_ENDPOINT, + destinationEndpoint: Zdo.ZDO_ENDPOINT, + options, groupId: 0, sequence: 0,// set by stack }; const messageContents = this.zdoRequestBuffalo.getWritten(); - if (destination === BroadcastAddress.DEFAULT || destination === BroadcastAddress.RX_ON_WHEN_IDLE - || destination === BroadcastAddress.SLEEPY) { + if (destination === ZSpec.BroadcastAddress.DEFAULT || destination === ZSpec.BroadcastAddress.RX_ON_WHEN_IDLE + || destination === ZSpec.BroadcastAddress.SLEEPY) { logger.debug(`~~~> [ZDO BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS); const [status, apsSequence] = (await this.ezsp.ezspSendBroadcast( ZSpec.NULL_NODE_ID,// alias @@ -2046,14 +2008,14 @@ export class EmberAdapter extends Adapter { options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { // 2 bytes for NWK Address + 2 bytes for Profile Id + 1 byte for in Cluster Count // + in times 2 for 2 byte Clusters + out Cluster Count + out times 2 for 2 byte Clusters - const length = (ZDO_MESSAGE_OVERHEAD + 2 + 2 + 1 + (inClusters.length * 2) + 1 + (outClusters.length * 2)); + const length = (Zdo.ZDO_MESSAGE_OVERHEAD + 2 + 2 + 1 + (inClusters.length * 2) + 1 + (outClusters.length * 2)); // sanity check if (length > EZSP_MAX_FRAME_LENGTH) { return [SLStatus.MESSAGE_TOO_LONG, null, null]; } - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); this.zdoRequestBuffalo.writeUInt16(profile); @@ -2066,7 +2028,7 @@ export class EmberAdapter extends Adapter { `~~~> [ZDO MATCH_DESCRIPTORS_REQUEST target=${target} profile=${profile} inClusters=${inClusters} outClusters=${outClusters}]`, NS, ); - return this.sendZDORequestBuffer(target, MATCH_DESCRIPTORS_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.MATCH_DESCRIPTORS_REQUEST, options); } /** @@ -2088,14 +2050,14 @@ export class EmberAdapter extends Adapter { */ private async emberNetworkAddressRequest(target: EUI64, reportKids: boolean, childStartIndex: number) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(target); this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); this.zdoRequestBuffalo.writeUInt8(childStartIndex); logger.debug(`~~~> [ZDO NETWORK_ADDRESS_REQUEST target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(BroadcastAddress.RX_ON_WHEN_IDLE, NETWORK_ADDRESS_REQUEST, EmberApsOption.SOURCE_EUI64); + return this.sendZDORequestBuffer(ZSpec.BroadcastAddress.RX_ON_WHEN_IDLE, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, EmberApsOption.SOURCE_EUI64); } /** @@ -2118,14 +2080,14 @@ export class EmberAdapter extends Adapter { */ private async emberIeeeAddressRequest(target: NodeId, reportKids: boolean, childStartIndex: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); this.zdoRequestBuffalo.writeUInt8(childStartIndex); logger.debug(`~~~> [ZDO IEEE_ADDRESS_REQUEST target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(target, IEEE_ADDRESS_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, options); } /** @@ -2138,7 +2100,7 @@ export class EmberAdapter extends Adapter { */ private async emberIeeeAddressRequestToTarget(discoveryNodeId: NodeId, reportKids: boolean, childStartIndex: number, options: EmberApsOption, targetNodeIdOfRequest: NodeId): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(discoveryNodeId); this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); @@ -2146,7 +2108,7 @@ export class EmberAdapter extends Adapter { logger.debug(`~~~> [ZDO IEEE_ADDRESS_REQUEST targetNodeIdOfRequest=${targetNodeIdOfRequest} discoveryNodeId=${discoveryNodeId} ` + `reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(targetNodeIdOfRequest, IEEE_ADDRESS_REQUEST, options); + return this.sendZDORequestBuffer(targetNodeIdOfRequest, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, options); } /** @@ -2159,7 +2121,7 @@ export class EmberAdapter extends Adapter { */ private async emberSendZigDevRequestTarget(target: NodeId, clusterId: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); @@ -2187,13 +2149,13 @@ export class EmberAdapter extends Adapter { */ private async emberSimpleDescriptorRequest(target: NodeId, targetEndpoint: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt16(target); this.zdoRequestBuffalo.writeUInt8(targetEndpoint); logger.debug(`~~~> [ZDO SIMPLE_DESCRIPTOR_REQUEST target=${target} targetEndpoint=${targetEndpoint}]`, NS); - return this.sendZDORequestBuffer(target, SIMPLE_DESCRIPTOR_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, options); } /** @@ -2222,7 +2184,7 @@ export class EmberAdapter extends Adapter { private async emberSendZigDevBindRequest(target: NodeId, bindClusterId: number, source: EUI64, sourceEndpoint: number, clusterId: number, type: number, destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(source); this.zdoRequestBuffalo.writeUInt8(sourceEndpoint); @@ -2230,11 +2192,11 @@ export class EmberAdapter extends Adapter { this.zdoRequestBuffalo.writeUInt8(type); switch (type) { - case UNICAST_BINDING: + case Zdo.UNICAST_BINDING: this.zdoRequestBuffalo.writeIeeeAddr(destination); this.zdoRequestBuffalo.writeUInt8(destinationEndpoint); break; - case MULTICAST_BINDING: + case Zdo.MULTICAST_BINDING: this.zdoRequestBuffalo.writeUInt16(groupAddress); break; default: @@ -2280,7 +2242,7 @@ export class EmberAdapter extends Adapter { + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`, NS); return this.emberSendZigDevBindRequest( target, - BIND_REQUEST, + Zdo.ClusterId.BIND_REQUEST, source, sourceEndpoint, clusterId, @@ -2331,7 +2293,7 @@ export class EmberAdapter extends Adapter { ); return this.emberSendZigDevBindRequest( target, - UNBIND_REQUEST, + Zdo.ClusterId.UNBIND_REQUEST, source, sourceEndpoint, clusterId, @@ -2359,7 +2321,7 @@ export class EmberAdapter extends Adapter { private async emberActiveEndpointsRequest(target: NodeId, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO ACTIVE_ENDPOINTS_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, ACTIVE_ENDPOINTS_REQUEST, options); + return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, options); } /** @@ -2381,7 +2343,7 @@ export class EmberAdapter extends Adapter { private async emberPowerDescriptorRequest(target: NodeId, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO POWER_DESCRIPTOR_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, POWER_DESCRIPTOR_REQUEST, options); + return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.POWER_DESCRIPTOR_REQUEST, options); } /** @@ -2402,7 +2364,7 @@ export class EmberAdapter extends Adapter { private async emberNodeDescriptorRequest(target: NodeId, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO NODE_DESCRIPTOR_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, NODE_DESCRIPTOR_REQUEST, options); + return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, options); } /** @@ -2425,7 +2387,7 @@ export class EmberAdapter extends Adapter { private async emberLqiTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO LQI_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(LQI_TABLE_REQUEST, target, startIndex, options); + return this.emberTableRequest(Zdo.ClusterId.LQI_TABLE_REQUEST, target, startIndex, options); } /** @@ -2448,7 +2410,7 @@ export class EmberAdapter extends Adapter { private async emberRoutingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO ROUTING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(ROUTING_TABLE_REQUEST, target, startIndex, options); + return this.emberTableRequest(Zdo.ClusterId.ROUTING_TABLE_REQUEST, target, startIndex, options); } /** @@ -2472,7 +2434,7 @@ export class EmberAdapter extends Adapter { private async emberBindingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { logger.debug(`~~~> [ZDO BINDING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(BINDING_TABLE_REQUEST, target, startIndex, options); + return this.emberTableRequest(Zdo.ClusterId.BINDING_TABLE_REQUEST, target, startIndex, options); } /** @@ -2486,7 +2448,7 @@ export class EmberAdapter extends Adapter { */ private async emberTableRequest(clusterId: number, target: NodeId, startIndex: number, options: EmberApsOption) : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt8(startIndex); @@ -2513,13 +2475,13 @@ export class EmberAdapter extends Adapter { */ private async emberLeaveRequest(target: NodeId, deviceAddress: EUI64, leaveRequestFlags: number, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeIeeeAddr(deviceAddress); this.zdoRequestBuffalo.writeUInt8(leaveRequestFlags); logger.debug(`~~~> [ZDO LEAVE_REQUEST target=${target} deviceAddress=${deviceAddress} leaveRequestFlags=${leaveRequestFlags}]`, NS); - return this.sendZDORequestBuffer(target, LEAVE_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.LEAVE_REQUEST, options); } /** @@ -2541,13 +2503,13 @@ export class EmberAdapter extends Adapter { */ private async emberPermitJoiningRequest(target: NodeId, duration: number, authentication: number, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt8(duration); this.zdoRequestBuffalo.writeUInt8(authentication); logger.debug(`~~~> [ZDO PERMIT_JOINING_REQUEST target=${target} duration=${duration} authentication=${authentication}]`, NS); - return this.sendZDORequestBuffer(target, PERMIT_JOINING_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.PERMIT_JOINING_REQUEST, options); } /** @@ -2563,7 +2525,7 @@ export class EmberAdapter extends Adapter { */ private async emberNetworkUpdateRequest(target: NodeId, scanChannels: number[], duration: number, count: number | null, manager: NodeId | null, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); + this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); this.zdoRequestBuffalo.writeUInt32(scanChannels.reduce((a, c) => a + (1 << c), 0));// to uint32_t this.zdoRequestBuffalo.writeUInt8(duration); @@ -2580,7 +2542,7 @@ export class EmberAdapter extends Adapter { `~~~> [ZDO NWK_UPDATE_REQUEST target=${target} scanChannels=${scanChannels} duration=${duration} count=${count} manager=${manager}]`, NS, ); - return this.sendZDORequestBuffer(target, NWK_UPDATE_REQUEST, options); + return this.sendZDORequestBuffer(target, Zdo.ClusterId.NWK_UPDATE_REQUEST, options); } private async emberScanChannelsRequest(target: NodeId, scanChannels: number[], duration: number, count: number, options: EmberApsOption): @@ -2830,7 +2792,7 @@ export class EmberAdapter extends Adapter { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, apsFrame, messageTag] = (await this.emberChannelChangeRequest( - BroadcastAddress.SLEEPY, + ZSpec.BroadcastAddress.SLEEPY, newChannel, DEFAULT_APS_OPTIONS, )); @@ -2854,6 +2816,41 @@ export class EmberAdapter extends Adapter { }); } + // queued + public async scanChannels(networkAddress: NodeId, channels: number[], duration: number, count: number): Promise { + return new Promise((resolve, reject): void => { + this.requestQueue.enqueue( + async (): Promise => { + this.checkInterpanLock(); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = (await this.emberScanChannelsRequest( + networkAddress, + channels, + duration, + count, + DEFAULT_APS_OPTIONS, + )); + + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed to scan channels '${channels}' on '${networkAddress} with status=${SLStatus[status]}.`, NS); + return status; + } + + const result = await this.oneWaitress.startWaitingFor({ + target: networkAddress, + apsFrame, + responseClusterId: Zdo.ClusterId.NWK_UPDATE_RESPONSE, + }, DEFAULT_ZDO_REQUEST_TIMEOUT + (((((2 ** duration) + 1) * (16 * 960)) / 1000) * count * channels.length));// time for scan + + resolve(result); + return SLStatus.OK; + }, + reject, + ); + }); + } + // queued public async setTransmitPower(value: number): Promise { if (typeof value !== 'number') { @@ -3033,7 +3030,7 @@ export class EmberAdapter extends Adapter { (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: PERMIT_JOINING_RESPONSE, + responseClusterId: Zdo.ClusterId.PERMIT_JOINING_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); resolve(); @@ -3108,16 +3105,16 @@ export class EmberAdapter extends Adapter { return [reqStatus, null, null]; } - const result = (await this.oneWaitress.startWaitingFor({ + const result = (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: LQI_TABLE_RESPONSE, + responseClusterId: Zdo.ClusterId.LQI_TABLE_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); for (const entry of result.entryList) { neighbors.push({ ieeeAddr: entry.eui64, - networkAddress: entry.nodeId, + networkAddress: entry.nwkAddress, linkquality: entry.lqi, relationship: entry.relationship, depth: entry.depth, @@ -3175,10 +3172,10 @@ export class EmberAdapter extends Adapter { return [reqStatus, null, null]; } - const result = (await this.oneWaitress.startWaitingFor({ + const result = (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: ROUTING_TABLE_RESPONSE, + responseClusterId: Zdo.ClusterId.ROUTING_TABLE_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); for (const entry of result.entryList) { @@ -3239,10 +3236,10 @@ export class EmberAdapter extends Adapter { return status; } - const result = (await this.oneWaitress.startWaitingFor({ + const result = (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: NODE_DESCRIPTOR_RESPONSE, + responseClusterId: Zdo.ClusterId.NODE_DESCRIPTOR_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); let type: TsType.DeviceType = 'Unknown'; @@ -3260,10 +3257,10 @@ export class EmberAdapter extends Adapter { } // always 0 before rev. 21 where field was added - if (result.stackRevision < CURRENT_ZIGBEE_SPEC_REVISION) { - logger.warning(`[ZDO] Node descriptor for "${networkAddress}" reports device is only compliant to revision ` - + `"${(result.stackRevision < 21) ? 'pre-21' : result.stackRevision}" of the ZigBee specification ` - + `(current revision: ${CURRENT_ZIGBEE_SPEC_REVISION}).`, NS); + if (result.serverMask.stackComplianceResivion < CURRENT_ZIGBEE_SPEC_REVISION) { + logger.warning(`[ZDO] Node descriptor for '${networkAddress}' reports device is only compliant to revision ` + + `'${(result.serverMask.stackComplianceResivion < 21) ? 'pre-21' : result.serverMask.stackComplianceResivion}' ` + + `of the ZigBee specification (current revision: ${CURRENT_ZIGBEE_SPEC_REVISION}).`, NS); } resolve({type, manufacturerCode: result.manufacturerCode}); @@ -3290,10 +3287,10 @@ export class EmberAdapter extends Adapter { return status; } - const result = (await this.oneWaitress.startWaitingFor({ + const result = (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: ACTIVE_ENDPOINTS_RESPONSE, + responseClusterId: Zdo.ClusterId.ACTIVE_ENDPOINTS_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); resolve({endpoints: result.endpointList}); @@ -3325,10 +3322,10 @@ export class EmberAdapter extends Adapter { return status; } - const result = (await this.oneWaitress.startWaitingFor({ + const result = (await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: SIMPLE_DESCRIPTOR_RESPONSE, + responseClusterId: Zdo.ClusterId.SIMPLE_DESCRIPTOR_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT)); resolve({ @@ -3362,7 +3359,7 @@ export class EmberAdapter extends Adapter { sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, - UNICAST_BINDING, + Zdo.UNICAST_BINDING, destinationAddressOrGroup as EUI64, null,// doesn't matter destinationEndpoint, @@ -3378,7 +3375,7 @@ export class EmberAdapter extends Adapter { await this.oneWaitress.startWaitingFor({ target: destinationNetworkAddress, apsFrame, - responseClusterId: BIND_RESPONSE, + responseClusterId: Zdo.ClusterId.BIND_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); @@ -3400,7 +3397,7 @@ export class EmberAdapter extends Adapter { sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, - MULTICAST_BINDING, + Zdo.MULTICAST_BINDING, null,// doesn't matter destinationAddressOrGroup, destinationEndpoint,// doesn't matter @@ -3416,7 +3413,7 @@ export class EmberAdapter extends Adapter { await this.oneWaitress.startWaitingFor({ target: destinationNetworkAddress, apsFrame, - responseClusterId: BIND_RESPONSE, + responseClusterId: Zdo.ClusterId.BIND_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); @@ -3445,7 +3442,7 @@ export class EmberAdapter extends Adapter { sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, - UNICAST_BINDING, + Zdo.UNICAST_BINDING, destinationAddressOrGroup as EUI64, null,// doesn't matter destinationEndpoint, @@ -3461,7 +3458,7 @@ export class EmberAdapter extends Adapter { await this.oneWaitress.startWaitingFor({ target: destinationNetworkAddress, apsFrame, - responseClusterId: UNBIND_RESPONSE, + responseClusterId: Zdo.ClusterId.UNBIND_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); @@ -3484,7 +3481,7 @@ export class EmberAdapter extends Adapter { sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, - MULTICAST_BINDING, + Zdo.MULTICAST_BINDING, null,// doesn't matter destinationAddressOrGroup, destinationEndpoint,// doesn't matter @@ -3500,7 +3497,7 @@ export class EmberAdapter extends Adapter { await this.oneWaitress.startWaitingFor({ target: destinationNetworkAddress, apsFrame, - responseClusterId: UNBIND_RESPONSE, + responseClusterId: Zdo.ClusterId.UNBIND_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); @@ -3537,7 +3534,7 @@ export class EmberAdapter extends Adapter { await this.oneWaitress.startWaitingFor({ target: networkAddress, apsFrame, - responseClusterId: LEAVE_RESPONSE, + responseClusterId: Zdo.ClusterId.LEAVE_RESPONSE, }, DEFAULT_ZDO_REQUEST_TIMEOUT); resolve(); @@ -3673,7 +3670,8 @@ export class EmberAdapter extends Adapter { } // queued, non-InterPAN - public async sendZclFrameToAll(endpoint: number, zclFrame: Zcl.Frame, sourceEndpoint: number, destination: BroadcastAddress): Promise { + public async sendZclFrameToAll(endpoint: number, zclFrame: Zcl.Frame, sourceEndpoint: number, destination: ZSpec.BroadcastAddress) + : Promise { const sourceEndpointInfo = typeof sourceEndpoint === 'number' ? FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : FIXED_ENDPOINTS[0]; const apsFrame: EmberApsFrame = { @@ -3808,7 +3806,7 @@ export class EmberAdapter extends Adapter { sourceEndpoint: 0, destinationEndpoint: 0, options: EmberApsOption.NONE, - groupId: BroadcastAddress.SLEEPY, + groupId: ZSpec.BroadcastAddress.SLEEPY, sequence: 0,// set by stack }; diff --git a/src/adapter/ember/adapter/oneWaitress.ts b/src/adapter/ember/adapter/oneWaitress.ts index 13b799f20b..7ef275dfac 100644 --- a/src/adapter/ember/adapter/oneWaitress.ts +++ b/src/adapter/ember/adapter/oneWaitress.ts @@ -4,10 +4,7 @@ import {ZclPayload} from '../../events'; import {NodeId} from '../../../zspec/tstypes'; import {TOUCHLINK_PROFILE_ID} from '../../../zspec/consts'; import {EmberApsFrame} from '../types'; -import {EmberZdoStatus} from '../zdo'; -import {logger} from '../../../utils/logger'; - -const NS = 'zh:ember:waitress'; +import * as Zdo from '../../../zspec/zdo/'; /** Events specific to OneWaitress usage. */ export enum OneWaitressEvents { @@ -111,7 +108,7 @@ export class EmberOneWaitress { * @param payload * @returns */ - public resolveZDO(status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: unknown): boolean { + public resolveZDO(sender: NodeId, apsFrame: EmberApsFrame, payload: unknown | Zdo.StatusError): boolean { for (const [index, waiter] of this.waiters.entries()) { if (waiter.timedout) { this.waiters.delete(index); @@ -128,16 +125,10 @@ export class EmberOneWaitress { this.waiters.delete(index); - if (status === EmberZdoStatus.ZDP_SUCCESS) { - waiter.resolve(payload); - } else if (status === EmberZdoStatus.ZDP_NO_ENTRY) { - // XXX: bypassing fail here since Z2M seems to trigger ZDO remove-type commands without checking current state - // Z2M also fails with ZCL payload NOT_FOUND though. This should be removed once upstream fixes that. - logger.info(`[ZDO] Received status ZDP_NO_ENTRY for "${sender}" cluster "${apsFrame.clusterId}". Ignoring.`, NS); - waiter.resolve(payload); + if (payload instanceof Zdo.StatusError || payload instanceof Error) { + waiter.reject(new Error(`[ZDO] Failed response for '${sender}' cluster '${apsFrame.clusterId}' ${payload.message}.`)); } else { - waiter.reject(new Error(`[ZDO] Failed response by NCP for "${sender}" cluster "${apsFrame.clusterId}" ` - + `with status=${EmberZdoStatus[status]}.`)); + waiter.resolve(payload); } return true; diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 49d0fdfe46..58adb24f25 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -4,7 +4,8 @@ import {SerialPortOptions} from "../../tstype"; import {Clusters} from "../../../zspec/zcl/definition/cluster"; import * as ZSpec from "../../../zspec"; import * as Zdo from "../../../zspec/zdo"; -import {byteToBits, getMacCapFlags, highByte, highLowToInt, lowByte, lowHighBits} from "../utils/math"; +import {BuffaloZdo} from "../../../zspec/zdo/buffaloZdo"; +import {highByte, highLowToInt, lowByte} from "../utils/math"; import { EmberOutgoingMessageType, EmberCounterType, @@ -88,42 +89,6 @@ import { EmberMultiprotocolPriorities, EmberRxPacketInfo, } from "../types"; -import { - EmberZdoStatus, - ACTIVE_ENDPOINTS_RESPONSE, - BINDING_TABLE_RESPONSE, - BIND_RESPONSE, - IEEE_ADDRESS_RESPONSE, - LEAVE_RESPONSE, - LQI_TABLE_RESPONSE, - MATCH_DESCRIPTORS_RESPONSE, - NETWORK_ADDRESS_RESPONSE, - NODE_DESCRIPTOR_RESPONSE, - PERMIT_JOINING_RESPONSE, - POWER_DESCRIPTOR_RESPONSE, - ROUTING_TABLE_RESPONSE, - SIMPLE_DESCRIPTOR_RESPONSE, - UNBIND_RESPONSE, - ZDO_MESSAGE_OVERHEAD, - ZDO_PROFILE_ID, - END_DEVICE_ANNOUNCE, - PARENT_ANNOUNCE_RESPONSE, - ZDOLQITableEntry, - ZDORoutingTableEntry, - ZDOBindingTableEntry, - IEEEAddressResponsePayload, - NetworkAddressResponsePayload, - MatchDescriptorsResponsePayload, - SimpleDescriptorResponsePayload, - NodeDescriptorResponsePayload, - PowerDescriptorResponsePayload, - ActiveEndpointsResponsePayload, - LQITableResponsePayload, - RoutingTableResponsePayload, - BindingTableResponsePayload, - EndDeviceAnnouncePayload, - ParentAnnounceResponsePayload -} from "../zdo"; import { EZSP_FRAME_CONTROL_ASYNCH_CB, EZSP_FRAME_CONTROL_INDEX, @@ -223,7 +188,7 @@ export enum EzspEvents { NCP_NEEDS_RESET_AND_INIT = 'NCP_NEEDS_RESET_AND_INIT', //-- ezspIncomingMessageHandler - /** params => status: EmberZdoStatus, sender: NodeId, apsFrame: EmberApsFrame, payload: { cluster-dependent @see zdo.ts } */ + /** params => sender: NodeId, apsFrame: EmberApsFrame, payload: depends on apsFrame.clusterId */ ZDO_RESPONSE = 'ZDO_RESPONSE', /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: NodeId, messageContents: Buffer */ INCOMING_MESSAGE = 'INCOMING_MESSAGE', @@ -5220,499 +5185,15 @@ export class Ezsp extends EventEmitter { NS, ); - if (apsFrame.profileId === ZDO_PROFILE_ID) { - const zdoBuffalo = new EzspBuffalo(messageContents, ZDO_MESSAGE_OVERHEAD);// set pos to skip `transaction sequence number` - - switch (apsFrame.clusterId) { - case IEEE_ADDRESS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - // 64-bit address for the remote device - const eui64 = zdoBuffalo.readIeeeAddr(); - // 16-bit address for the remote device - const nodeId = zdoBuffalo.readUInt16(); - // valid range 0x00-0xFF, count of the number of 16-bit shot addresses to follow. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, should be 0 - // if an error occurs or the RequestType in the request is for a Single Device Response, - // this fiel is not included in the frame. - let assocDevCount: number = 0; - // 0x00-0xFF, starting index into the list of assoc. devices for this report. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, - // this field is not included in the frame, same if error, or RequestType is for Single Device Response - let startIndex: number = 0; - // list of 0x0000-0xFFFF, one corresponds to each assoc. device to the remote device. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, - // this field is not included in the frame, same if error, or RequestType is for Single Device Response - let assocDevList: number[] = []; - - if (zdoBuffalo.isMore()) { - assocDevCount = zdoBuffalo.readUInt8(); - startIndex = zdoBuffalo.readUInt8(); - - assocDevList = zdoBuffalo.readListUInt16(assocDevCount); - } - - logger.debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE status=${EmberZdoStatus[status]} eui64=${eui64} nodeId=${nodeId} ` - + `startIndex=${startIndex} assocDevList=${assocDevList}]`, NS); - logger.debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE] Support not implemented upstream`, NS); - - const payload: IEEEAddressResponsePayload = {eui64, nodeId, assocDevList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case NETWORK_ADDRESS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO NETWORK_ADDRESS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - // 64-bit address for the remote device - const eui64 = zdoBuffalo.readIeeeAddr(); - // 16-bit address for the remote device - const nodeId = zdoBuffalo.readUInt16(); - // valid range 0x00-0xFF, count of the number of 16-bit shot addresses to follow. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, should be 0 - // if an error occurs or the RequestType in the request is for a Single Device Response, - // this fiel is not included in the frame. - let assocDevCount: number = 0; - // 0x00-0xFF, starting index into the list of assoc. devices for this report. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, - // this field is not included in the frame, same if error, or RequestType is for Single Device Response - let startIndex: number = 0; - // list of 0x0000-0xFFFF, one corresponds to each assoc. device to the remote device. - // if the RequestType in the request is Extended Response, and there are no assoc. devices on the remote device, - // this field is not included in the frame, same if error, or RequestType is for Single Device Response - let assocDevList: number[] = []; - - if (zdoBuffalo.isMore()) { - assocDevCount = zdoBuffalo.readUInt8(); - startIndex = zdoBuffalo.readUInt8(); - - assocDevList = zdoBuffalo.readListUInt16(assocDevCount); - } - - logger.debug(`<=== [ZDO NETWORK_ADDRESS_RESPONSE status=${EmberZdoStatus[status]} eui64=${eui64} nodeId=${nodeId} ` - + `startIndex=${startIndex} assocDevList=${assocDevList}]`, NS); - - const payload: NetworkAddressResponsePayload = {eui64, nodeId, assocDevList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case MATCH_DESCRIPTORS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - const endpointCount = zdoBuffalo.readUInt8(); - const endpointList = zdoBuffalo.readListUInt8(endpointCount); - - logger.debug( - `<=== [ZDO MATCH_DESCRIPTORS_RESPONSE status=${EmberZdoStatus[status]} nodeId=${nodeId} endpointList=${endpointList}]`, - NS, - ); - logger.debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE] Support not implemented upstream`, NS); - - const payload: MatchDescriptorsResponsePayload = {nodeId, endpointList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case SIMPLE_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO SIMPLE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const length = zdoBuffalo.readUInt8(); - const endpoint = zdoBuffalo.readUInt8(); - const profileId = zdoBuffalo.readUInt16(); - const deviceId = zdoBuffalo.readUInt16(); - // values 0000-1111, others reserved - const deviceVersion = zdoBuffalo.readUInt8(); - const inClusterCount = zdoBuffalo.readUInt8(); - const inClusterList = zdoBuffalo.readListUInt16(inClusterCount); - const outClusterCount = zdoBuffalo.readUInt8(); - const outClusterList = zdoBuffalo.readListUInt16(outClusterCount); - - logger.debug(`<=== [ZDO SIMPLE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]} nodeId=${nodeId} endpoint=${endpoint} ` - + `profileId=${profileId} deviceId=${deviceId} deviceVersion=${deviceVersion} inClusterList=${inClusterList} ` - + `outClusterList=${outClusterList}]`, NS); - - const payload: SimpleDescriptorResponsePayload = { - nodeId, - endpoint, - profileId, - deviceId, - inClusterList, - outClusterList, - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case NODE_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO NODE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - // in bits: [logical type: 3] [complex description available: 1] [user descriptor available: 1] [reserved/unused: 3] - const nodeDescByte1 = zdoBuffalo.readUInt8(); - // 000 == Zigbee Coordinator, 001 == Zigbee Router, 010 === Zigbee End Device, 011-111 === Reserved - const logicalType = (nodeDescByte1 & 0x07); - // 0 == not avail - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const complexDescAvail = (nodeDescByte1 & 0x08) >> 3; - // 0 == not avai - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const userDescAvail = (nodeDescByte1 & 0x10) >> 4; - // in bits: [aps flags: 3] [frequency band: 5] - const nodeDescByte2 = zdoBuffalo.readUInt8(); - // currently not supported, should be zero - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const apsFlags = (nodeDescByte2 & 0x07); - // 0 == 868 – 868.6 MHz BPSK, 1 == Reserved, 2 == 902 – 928 MHz BPSK, - // 3 == 2400 – 2483.5 MHz, 4 == European FSK sub-GHz bands: (863-876MHz and 915-921MHz) - const freqBand = (nodeDescByte2 & 0xF8) >> 3; - /** @see MACCapabilityFlags */ - const macCapFlags = zdoBuffalo.readUInt8(); - // allocated by Zigbee Alliance - const manufacturerCode = zdoBuffalo.readUInt16(); - // valid range 0x00-0x7F, max size in octets of the network sub-layer data unit (NSDU) for node. - // max size of data or commands passed to or from the app by the app support sub-layer before any fragmentation or re-assembly. - // can be used as a high-level indication for network management - const maxBufSize = zdoBuffalo.readUInt8(); - // valid range 0x0000-0x7FFF, max size in octets of the application sub-layer data unit (ASDU) - // that can be transferred to this node in one single message transfer. - // can exceed max buf size through use of fragmentation. - const maxIncTxSize = zdoBuffalo.readUInt16(); - // in bits: - // [primary trust center: 1] - // [backup trust center: 1] - // [primary binding table cache: 1] - // [backup binding table cache: 1] - // [primary discovery cache: 1] - // [backup discovery cache: 1] - // [network manager: 1] - // [reserved: 2] - // [stack compliance revision: 7] - const serverMask = zdoBuffalo.readUInt16(); - // revision of the Zigbee Pro Core specs implemented (always zeroed out prior to revision 21 that added these fields to the spec) - const stackRevision = (serverMask & 0xFE00) >> 9; - // valid range 0x0000-0x7FFF, max size in octets of the application sub-layer data uni (ASDU) - // that can be transferred from this node in one single message transfer. - // can exceed max buf size through use of fragmentation. - const maxOutTxSize = zdoBuffalo.readUInt16(); - // in bits: [extended active endpoint list available: 1] [extended simple descriptor list available: 1] [reserved: 6] - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const descCapFlags = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO NODE_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]} nodeId=${nodeId} logicalType=${logicalType} ` - + `freqBand=${freqBand} macCapFlags=${byteToBits(macCapFlags)} manufacturerCode=${manufacturerCode} maxBufSize=${maxBufSize} ` - + `maxIncTxSize=${maxIncTxSize} stackRevision=${stackRevision} maxOutTxSize=${maxOutTxSize}]`, NS); - - const payload: NodeDescriptorResponsePayload = { - nodeId, - logicalType, - macCapFlags: getMacCapFlags(macCapFlags), - manufacturerCode, - stackRevision - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case POWER_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - // mode: 0000 == receiver sync'ed with receiver on when idle subfield of the node descriptor - // 0001 == receiver comes on periodically as defined by the node power descriptor - // 0010 == receiver comes on when stimulated, for example, by a user pressing a button - // 0011-1111 reserved - // source (bits): 0 == constants (mains) power - // 1 == rechargeable battery - // 2 == disposable battery - // 3 == reserved - const [currentPowerMode, availPowerSources] = lowHighBits(zdoBuffalo.readUInt8()); - // source (bits): 0 == constants (mains) power - // 1 == rechargeable battery - // 2 == disposable battery - // 3 == reserved - // level: 0000 == critical - // 0100 == 33% - // 1000 == 66% - // 1100 == 100% - // All other values reserved - const [currentPowerSource, currentPowerSourceLevel] = lowHighBits(zdoBuffalo.readUInt8()); - - logger.debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE status=${EmberZdoStatus[status]} nodeId=${nodeId} ` - + `currentPowerMode=${currentPowerMode} availPowerSources=${availPowerSources} currentPowerSource=${currentPowerSource} ` - + `currentPowerSourceLevel=${currentPowerSourceLevel}]`, NS); - logger.debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE] Support not implemented upstream`, NS); - - const payload: PowerDescriptorResponsePayload = { - nodeId, - currentPowerMode, - availPowerSources, - currentPowerSource, - currentPowerSourceLevel - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case ACTIVE_ENDPOINTS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO ACTIVE_ENDPOINTS_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - const endpointCount = zdoBuffalo.readUInt8(); - const endpointList = zdoBuffalo.readListUInt8(endpointCount); - - logger.debug( - `<=== [ZDO ACTIVE_ENDPOINTS_RESPONSE status=${EmberZdoStatus[status]} nodeId=${nodeId} endpointList=${endpointList}]`, - NS, - ); - - const payload: ActiveEndpointsResponsePayload = {nodeId, endpointList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case LQI_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO LQI_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - // 0x00-0xFF, total number of neighbor table entries within the remote device - const neighborTableEntries = zdoBuffalo.readUInt8(); - // 0x00-0xFF, starting index within the neighbor table to begin reporting for the NeighborTableList - const startIndex = zdoBuffalo.readUInt8(); - // 0x00-0x02, number of neighbor table entries included within NeighborTableList - const entryCount = zdoBuffalo.readUInt8(); - // list of descriptors, beginning with the {startIndex} element and continuing for {entryCount} - // of the elements in the remote device's neighbor table, including the device address and assoc. LQI - const entryList: ZDOLQITableEntry[] = []; - - for (let i = 0; i < entryCount; i++) { - const extendedPanId = zdoBuffalo.readListUInt8(ZSpec.EXTENDED_PAN_ID_SIZE); - const eui64 = zdoBuffalo.readIeeeAddr(); - const nodeId = zdoBuffalo.readUInt16(); - const deviceTypeByte = zdoBuffalo.readUInt8(); - const permitJoiningByte = zdoBuffalo.readUInt8(); - const depth = zdoBuffalo.readUInt8(); - const lqi = zdoBuffalo.readUInt8(); - - entryList.push({ - extendedPanId, - eui64, - nodeId, - deviceType: deviceTypeByte & 0x03, - rxOnWhenIdle: (deviceTypeByte & 0x0C) >> 2, - relationship: (deviceTypeByte & 0x70) >> 4, - reserved1: (deviceTypeByte & 0x10) >> 7, - permitJoining: permitJoiningByte & 0x03, - reserved2: (permitJoiningByte & 0xFC) >> 2, - depth, - lqi, - }); - } - - logger.debug(`<=== [ZDO LQI_TABLE_RESPONSE status=${EmberZdoStatus[status]} neighborTableEntries=${neighborTableEntries} ` - + `startIndex=${startIndex} entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`, NS); - - const payload: LQITableResponsePayload = {neighborTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case ROUTING_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO ROUTING_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - // 0x00-0xFF, total number of routing table entries within the remote device - const routingTableEntries = zdoBuffalo.readUInt8(); - // 0x00-0xFF, starting index within the routing table to begin reporting for the RoutingTableList - const startIndex = zdoBuffalo.readUInt8(); - // 0x00-0xFF, number of routing table entries included within RoutingTableList - const entryCount = zdoBuffalo.readUInt8(); - // list of descriptors, beginning with the {startIndex} element and continuing for {entryCount} - // of the elements in the remote device's routing table - const entryList: ZDORoutingTableEntry[] = []; - - for (let i = 0; i < entryCount; i++) { - const destinationAddress = zdoBuffalo.readUInt16(); - const statusByte = zdoBuffalo.readUInt8(); - const nextHopAddress = zdoBuffalo.readUInt16(); - - entryList.push({ - destinationAddress, - status: statusByte & 0x07, - memoryConstrained: (statusByte & 0x08) >> 3, - manyToOne: (statusByte & 0x10) >> 4, - routeRecordRequired: (statusByte & 0x20) >> 5, - reserved: (statusByte & 0xC0) >> 6, - nextHopAddress, - }); - } - - logger.debug(`<=== [ZDO ROUTING_TABLE_RESPONSE status=${EmberZdoStatus[status]} routingTableEntries=${routingTableEntries} ` - + `startIndex=${startIndex} entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`, NS); - - const payload: RoutingTableResponsePayload = {routingTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case BINDING_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - logger.debug(`<=== [ZDO BINDING_TABLE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const bindingTableEntries = zdoBuffalo.readUInt8(); - const startIndex = zdoBuffalo.readUInt8(); - const entryCount = zdoBuffalo.readUInt8(); - const entryList: ZDOBindingTableEntry[] = []; - - for (let i = 0; i < entryCount; i++) { - const sourceEui64 = zdoBuffalo.readIeeeAddr(); - const sourceEndpoint = zdoBuffalo.readUInt8(); - const clusterId = zdoBuffalo.readUInt16(); - const destAddrMode = zdoBuffalo.readUInt8(); - const dest = (destAddrMode === 0x01) ? zdoBuffalo.readUInt16() : ((destAddrMode === 0x03) ? zdoBuffalo.readIeeeAddr() : null); - const destEndpoint = (destAddrMode === 0x03) ? zdoBuffalo.readUInt8() : null; - - entryList.push({ - sourceEui64, - sourceEndpoint, - clusterId, - destAddrMode, - dest, - destEndpoint, - }); - } - - logger.debug(`<=== [ZDO BINDING_TABLE_RESPONSE status=${EmberZdoStatus[status]} bindingTableEntries=${bindingTableEntries} ` - + `startIndex=${startIndex} entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`, NS); - logger.debug(`<=== [ZDO BINDING_TABLE_RESPONSE] Support not implemented upstream`, NS); - - const payload: BindingTableResponsePayload = {bindingTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - break; - } - case BIND_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO BIND_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); - break; - } - case UNBIND_RESPONSE:{ - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO UNBIND_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); - break; - } - case LEAVE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO LEAVE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); - break; - } - case PERMIT_JOINING_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO PERMIT_JOINING_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame); - break; - } - case END_DEVICE_ANNOUNCE: { - const nodeId = zdoBuffalo.readUInt16(); - const eui64 = zdoBuffalo.readIeeeAddr(); - /** @see MACCapabilityFlags */ - const capabilities = zdoBuffalo.readUInt8(); - - logger.debug(`<=== [ZDO END_DEVICE_ANNOUNCE nodeId=${nodeId} eui64=${eui64} capabilities=${byteToBits(capabilities)}]`, NS); - - const payload: EndDeviceAnnouncePayload = {nodeId, eui64, capabilities: getMacCapFlags(capabilities)}; - - // this one gets its own event since its purpose is to pass an event up to Z2M - this.emit(EzspEvents.END_DEVICE_ANNOUNCE, packetInfo.senderShortId, apsFrame, payload); - break; - } - case PARENT_ANNOUNCE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - // status should always be NOT_SUPPORTED here - logger.debug(`<=== [ZDO PARENT_ANNOUNCE_RESPONSE status=${EmberZdoStatus[status]}]`, NS); - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, null); - } else { - const numberOfChildren = zdoBuffalo.readUInt8(); - const children: EUI64[] = []; - - for (let i = 0; i < numberOfChildren; i++) { - const childEui64 = zdoBuffalo.readIeeeAddr(); + if (apsFrame.profileId === Zdo.ZDO_PROFILE_ID) { + try { + const payload = BuffaloZdo.readResponse(apsFrame.clusterId, messageContents); - children.push(childEui64); - } + logger.debug(`<=== [ZDO ${Zdo.ClusterId[apsFrame.clusterId]} ${payload ? JSON.stringify(payload) : ''}]`, NS); - logger.debug(`<=== [ZDO PARENT_ANNOUNCE_RESPONSE status=${EmberZdoStatus[status]} numberOfChildren=${numberOfChildren} ` - + `children=${children}]`, NS); - logger.debug(`<=== [ZDO PARENT_ANNOUNCE_RESPONSE] Support not implemented upstream`, NS); - - const payload: ParentAnnounceResponsePayload = {children}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, packetInfo.senderShortId, apsFrame, payload); - } - } - default: { - logger.info(`<=== [ZDO clusterId=${apsFrame.clusterId} sender=${packetInfo.senderShortId}] Support not implemented upstream.`, NS); - break; - } + this.emit(EzspEvents.ZDO_RESPONSE, packetInfo.senderShortId, apsFrame, payload); + } catch (error) { + this.emit(EzspEvents.ZDO_RESPONSE, packetInfo.senderShortId, apsFrame, error); } } else if (apsFrame.profileId === ZSpec.HA_PROFILE_ID || apsFrame.profileId === ZSpec.WILDCARD_PROFILE_ID) { this.emit(EzspEvents.INCOMING_MESSAGE, type, apsFrame, packetInfo.lastHopLqi, packetInfo.senderShortId, messageContents); diff --git a/src/adapter/ember/utils/math.ts b/src/adapter/ember/utils/math.ts index 98c3e6f9b7..b4cd3413a6 100644 --- a/src/adapter/ember/utils/math.ts +++ b/src/adapter/ember/utils/math.ts @@ -2,8 +2,6 @@ //-------------------------------------------------------------- // Define macros for handling 3-bit frame numbers modulo 8 -import {MACCapabilityFlags} from "../zdo"; - /** mask to frame number modulus */ export const mod8 = (n: number): number => n & 7; /** increment in frame number modulus */ @@ -75,22 +73,3 @@ export const lowHighBits = (n: number): [number, highBits: number] => [lowBits(n export const byteToBits = (n: number): string => { return (n >>> 0).toString(2).padStart(8, '0'); }; - -/** - * Get the values for the bitmap `Mac Capability Flags Field` as per spec. - * Given value is assumed to be a proper byte. - * @param capabilities - * @returns - */ -export const getMacCapFlags = (capabilities: number): MACCapabilityFlags => { - return { - alternatePANCoordinator: (capabilities & 0x01), - deviceType: (capabilities & 0x02) >> 1, - powerSource: (capabilities & 0x04) >> 2, - rxOnWhenIdle: (capabilities & 0x08) >> 3, - reserved1: (capabilities & 0x10) >> 4, - reserved2: (capabilities & 0x20) >> 5, - securityCapability: (capabilities & 0x40) >> 6, - allocateAddress: (capabilities & 0x80) >> 7, - }; -}; diff --git a/src/adapter/ember/zdo.ts b/src/adapter/ember/zdo.ts deleted file mode 100644 index e4531620a5..0000000000 --- a/src/adapter/ember/zdo.ts +++ /dev/null @@ -1,1039 +0,0 @@ -//------------------------------------------------------------------------------ -// ZigBee Device Object (ZDO) - -import {EUI64, ExtendedPanId, NodeId} from "../../zspec/tstypes"; - -/** The endpoint where the ZigBee Device Object (ZDO) resides. */ -export const ZDO_ENDPOINT = 0; - -/** The profile ID used by the ZigBee Device Object (ZDO). */ -export const ZDO_PROFILE_ID = 0x0000; - -/** ZDO messages start with a sequence number. */ -export const ZDO_MESSAGE_OVERHEAD = 1; - -/** - * ZDO response status. - * - * Most responses to ZDO commands contain a status byte. - * The meaning of this byte is defined by the ZigBee Device Profile. - * uint8_t - */ -export enum EmberZdoStatus { - // These values are taken from Table 48 of ZDP Errata 043238r003 and Table 2 - // of NWK 02130r10. - ZDP_SUCCESS = 0x00, - // 0x01 to 0x7F are reserved - ZDP_INVALID_REQUEST_TYPE = 0x80, - ZDP_DEVICE_NOT_FOUND = 0x81, - ZDP_INVALID_ENDPOINT = 0x82, - ZDP_NOT_ACTIVE = 0x83, - ZDP_NOT_SUPPORTED = 0x84, - ZDP_TIMEOUT = 0x85, - ZDP_NO_MATCH = 0x86, - // 0x87 is reserved = 0x87, - ZDP_NO_ENTRY = 0x88, - ZDP_NO_DESCRIPTOR = 0x89, - ZDP_INSUFFICIENT_SPACE = 0x8a, - ZDP_NOT_PERMITTED = 0x8b, - ZDP_TABLE_FULL = 0x8c, - ZDP_NOT_AUTHORIZED = 0x8d, - ZDP_DEVICE_BINDING_TABLE_FULL = 0x8e, - ZDP_INVALID_INDEX = 0x8f, - ZDP_FRAME_TOO_LARGE = 0x90, - ZDP_BAD_KEY_NEGOTIATION_METHOD = 0x91, - ZDP_TEMPORARY_FAILURE = 0x92, - - APS_SECURITY_FAIL = 0xad, - - NWK_ALREADY_PRESENT = 0xc5, - NWK_TABLE_FULL = 0xc7, - NWK_UNKNOWN_DEVICE = 0xc8, - - NWK_MISSING_TLV = 0xd6, - NWK_INVALID_TLV = 0xd7, -}; - -export type MACCapabilityFlags = { - /** - * The alternate PAN coordinator sub-field is one bit in length and shall be set to 1 if this node is capable of becoming a PAN coordinator. - * Otherwise, the alternative PAN coordinator sub-field shall be set to 0. - */ - alternatePANCoordinator: number, - /** - * The device type sub-field is one bit in length and shall be set to 1 if this node is a full function device (FFD). - * Otherwise, the device type sub-field shall be set to 0, indicating a reduced function device (RFD). - */ - deviceType: number, - /** - * The power source sub-field is one bit in length and shall be set to 1 if the current power source is mains power. - * Otherwise, the power source sub-field shall be set to 0. - * This information is derived from the node current power source field of the node power descriptor. - */ - powerSource: number, - /** - * The receiver on when idle sub-field is one bit in length and shall be set to 1 if the device does not disable its receiver to - * conserve power during idle periods. - * Otherwise, the receiver on when idle sub-field shall be set to 0 (see also section 2.3.2.4.) - */ - rxOnWhenIdle: number, - /** reserved */ - reserved1: number, - /** reserved */ - reserved2: number, - /** - * The security capability sub-field is one bit in length and shall be set to 1 if the device is capable of sending and receiving - * frames secured using the security suite specified in [B1]. - * Otherwise, the security capability sub-field shall be set to 0. - */ - securityCapability: number, - /** The allocate address sub-field is one bit in length and shall be set to 0 or 1. */ - allocateAddress: number, -}; - -export type ZDOLQITableEntry = { - /** - * The 64-bit extended PAN identifier of the neighboring device. - * - * 64-bit - */ - extendedPanId: ExtendedPanId, - /** - * 64-bit IEEE address that is unique to every device. - * If this value is unknown at the time of the request, this field shall be set to 0xffffffffffffffff. - * - * 64-bit - */ - eui64: EUI64, - /** The 16-bit network address of the neighboring device. 16-bit */ - nodeId: NodeId, - /** - * The type of the neighbor device: - * 0x00 = ZigBee coordinator - * 0x01 = ZigBee router - * 0x02 = ZigBee end device - * 0x03 = Unknown - * - * 2-bit - */ - deviceType: number, - /** - * Indicates if neighbor's receiver is enabled during idle portions of the CAP: - * 0x00 = Receiver is off - * 0x01 = Receiver is on - * 0x02 = unknown - * - * 2-bit - */ - rxOnWhenIdle: number, - /** - * The relationship between the neighbor and the current device: - * 0x00 = neighbor is the parent - * 0x01 = neighbor is a child - * 0x02 = neighbor is a sibling - * 0x03 = None of the above - * 0x04 = previous child - * - * 3-bit - */ - relationship: number, - /** This reserved bit shall be set to 0. 1-bit */ - reserved1: number, - /** - * An indication of whether the neighbor device is accepting join requests: - * 0x00 = neighbor is not accepting join requests - * 0x01 = neighbor is accepting join requests - * 0x02 = unknown - * - * 2-bit - */ - permitJoining: number, - /** Each of these reserved bits shall be set to 0. 6-bit */ - reserved2: number, - /** - * The tree depth of the neighbor device. - * A value of 0x00 indicates that the device is the ZigBee coordinator for the network - * - * 8-bit - */ - depth: number, - /** - * The estimated link quality for RF transmissions from this device. - * See [B1] for discussion of how this is calculated. - * - * 8-bit - */ - lqi: number, -}; - -export type ZDORoutingTableEntry = { - /** 16-bit network address of this route */ - destinationAddress: NodeId, - /** - * Status of the route - * 0x0=ACTIVE. - * 0x1=DISCOVERY_UNDERWAY. - * 0x2=DISCOVERY_FAILED. - * 0x3=INACTIVE. - * 0x4=VALIDATION_UNDERWAY - * 0x5-0x7=RESERVED - * - * 3-bit - */ - status: number, - /** - * A flag indicating whether the device is a memory constrained concentrator - * - * 1-bit - */ - memoryConstrained: number, - /** - * A flag indicating that the destination is a concentrator that issued a many-to-one request - * - * 1-bit - */ - manyToOne: number, - /** - * A flag indicating that a route record command frame should be sent to the destination prior to the next data packet. - * - * 1-bit - */ - routeRecordRequired: number, - /** 2-bit */ - reserved: number, - /** 16-bit network address of the next hop on the way to the destination. */ - nextHopAddress: NodeId, -}; - -export type ZDOBindingTableEntry = { - /** The source IEEE address for the binding entry. */ - sourceEui64: EUI64, - /** The source endpoint for the binding entry. uint8_t */ - sourceEndpoint: number, - /** The identifier of the cluster on the source device that is bound to the destination device. uint16_t */ - clusterId: number, - /** - * The addressing mode for the destination address. This field can take one of the non-reserved values from the following list: - * - 0x00 = reserved - * - 0x01 = 16-bit group address for DstAddr and DstEndpoint not present - * - 0x02 = reserved - * - 0x03 = 64-bit extended address for DstAddr and DstEndp present - * - 0x04 – 0xff = reserved - * - * uint8_t - */ - destAddrMode: number, - /** The destination address for the binding entry. uint16_t or uint8_t[EUI64_SIZE] */ - dest: NodeId | EUI64, - /** - * This field shall be present only if the DstAddrMode field has a value of 0x03 and, if present, - * shall be the destination endpoint for the binding entry. - * uint8_t or not present - */ - destEndpoint?: number, -}; - -/** @see IEEE_ADDRESS_RESPONSE */ -export type IEEEAddressResponsePayload = { - eui64: EUI64, - nodeId: NodeId, - assocDevList: number[], -}; - -/** @see NETWORK_ADDRESS_RESPONSE */ -export type NetworkAddressResponsePayload = { - eui64: EUI64, - nodeId: NodeId, - assocDevList: number[], -}; - -/** @see MATCH_DESCRIPTORS_RESPONSE */ -export type MatchDescriptorsResponsePayload = { - nodeId: NodeId, - endpointList: number[], -}; - -/** @see SIMPLE_DESCRIPTOR_RESPONSE */ -export type SimpleDescriptorResponsePayload = { - nodeId: NodeId, - /** uint8_t */ - // inClusterCount: number, - /** const uint16_t* */ - inClusterList: number[], - /** uint8_t */ - // outClusterCount: number, - /** const uint16_t* */ - outClusterList: number[], - /** uint18_t */ - profileId: number, - /** uint16_t */ - deviceId: number, - /** uint8_t */ - endpoint: number, -}; - -/** @see NODE_DESCRIPTOR_RESPONSE */ -export type NodeDescriptorResponsePayload = { - nodeId: NodeId, - logicalType: number, - macCapFlags: MACCapabilityFlags, - manufacturerCode: number, - stackRevision: number, -}; - -/** @see POWER_DESCRIPTOR_RESPONSE */ -export type PowerDescriptorResponsePayload = { - nodeId: NodeId, - currentPowerMode: number, - availPowerSources: number, - currentPowerSource: number, - currentPowerSourceLevel: number, -}; - -/** @see ACTIVE_ENDPOINTS_RESPONSE */ -export type ActiveEndpointsResponsePayload = { - nodeId: NodeId, - endpointList: number[], -}; - -/** @see LQI_TABLE_RESPONSE */ -export type LQITableResponsePayload = { - neighborTableEntries: number, - entryList: ZDOLQITableEntry[], -}; - -/** @see ROUTING_TABLE_RESPONSE */ -export type RoutingTableResponsePayload = { - routingTableEntries: number, - entryList: ZDORoutingTableEntry[], -}; - -/** @see BINDING_TABLE_RESPONSE */ -export type BindingTableResponsePayload = { - bindingTableEntries: number, - entryList: ZDOBindingTableEntry[], -}; - -/** @see END_DEVICE_ANNOUNCE */ -export type EndDeviceAnnouncePayload = { - nodeId: NodeId, - eui64: EUI64, - capabilities: MACCapabilityFlags, -}; - -/** @see PARENT_ANNOUNCE_RESPONSE */ -export type ParentAnnounceResponsePayload = { - children: EUI64[], -}; - -/** - * Defines for ZigBee device profile cluster IDs follow. These - * include descriptions of the formats of the messages. - * - * Note that each message starts with a 1-byte transaction sequence - * number. This sequence number is used to match a response command frame - * to the request frame that it is replying to. The application shall - * maintain a 1-byte counter that is copied into this field and incremented - * by one for each command sent. When a value of 0xff is reached, the next - * command shall re-start the counter with a value of 0x00. - */ - -// Network and IEEE Address Request/Response -/** - * Network request: [transaction sequence number: 1] - * [EUI64:8] [type:1] [start index:1] - */ -export const NETWORK_ADDRESS_REQUEST = 0x0000; -/** - * Response: [transaction sequence number: 1] - * [status:1] [EUI64:8] [node ID:2] - * [ID count:1] [start index:1] [child ID:2]* - */ -export const NETWORK_ADDRESS_RESPONSE = 0x8000; -/** - * IEEE request: [transaction sequence number: 1] - * [node ID:2] [type:1] [start index:1] - * [type] = 0x00 single address response, ignore the start index - * = 0x01 extended response -] sends kid's IDs as well - */ -export const IEEE_ADDRESS_REQUEST = 0x0001; -/** - * Response: [transaction sequence number: 1] - * [status:1] [EUI64:8] [node ID:2] - * [ID count:1] [start index:1] [child ID:2]* - */ -export const IEEE_ADDRESS_RESPONSE = 0x8001; - -// Node Descriptor Request/Response -/** - * Request: [transaction sequence number: 1] [node ID:2] [tlvs: varies] - */ -export const NODE_DESCRIPTOR_REQUEST = 0x0002; -/** - * Response: [transaction sequence number: 1] [status:1] [node ID:2] - * [node descriptor: 13] [tlvs: varies] - * - * Node Descriptor field is divided into subfields of bitmasks as follows: - * (Note: All lengths below are given in bits rather than bytes.) - * Logical Type: 3 - * Complex Descriptor Available: 1 - * User Descriptor Available: 1 - * (reserved/unused): 3 - * APS Flags: 3 - * Frequency Band: 5 - * MAC capability flags: 8 - * Manufacturer Code: 16 - * Maximum buffer size: 8 - * Maximum incoming transfer size: 16 - * Server mask: 16 - * Maximum outgoing transfer size: 16 - * Descriptor Capability Flags: 8 - * See ZigBee document 053474, Section 2.3.2.3 for more details. - */ -export const NODE_DESCRIPTOR_RESPONSE = 0x8002; - -// Power Descriptor Request / Response -/** - * - * Request: [transaction sequence number: 1] [node ID:2] - */ -export const POWER_DESCRIPTOR_REQUEST = 0x0003; -/** - * Response: [transaction sequence number: 1] [status:1] [node ID:2] - * [current power mode, available power sources:1] - * [current power source, current power source level:1] - * See ZigBee document 053474, Section 2.3.2.4 for more details. - */ -export const POWER_DESCRIPTOR_RESPONSE = 0x8003; - -// Simple Descriptor Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [node ID:2] [endpoint:1] - */ -export const SIMPLE_DESCRIPTOR_REQUEST = 0x0004; -/** - * Response: [transaction sequence number: 1] - * [status:1] [node ID:2] [length:1] [endpoint:1] - * [app profile ID:2] [app device ID:2] - * [app device version, app flags:1] - * [input cluster count:1] [input cluster:2]* - * [output cluster count:1] [output cluster:2]* - */ -export const SIMPLE_DESCRIPTOR_RESPONSE = 0x8004; - -// Active Endpoints Request / Response -/** - * - * Request: [transaction sequence number: 1] [node ID:2] - */ -export const ACTIVE_ENDPOINTS_REQUEST = 0x0005; -/** - * Response: [transaction sequence number: 1] - * [status:1] [node ID:2] [endpoint count:1] [endpoint:1]* - */ -export const ACTIVE_ENDPOINTS_RESPONSE = 0x8005; - -// Match Descriptors Request / Response -/** - * Request: [transaction sequence number: 1] - * [node ID:2] [app profile ID:2] - * [input cluster count:1] [input cluster:2]* - * [output cluster count:1] [output cluster:2]* - */ -export const MATCH_DESCRIPTORS_REQUEST = 0x0006; -/** - * Response: [transaction sequence number: 1] - * [status:1] [node ID:2] [endpoint count:1] [endpoint:1]* - */ -export const MATCH_DESCRIPTORS_RESPONSE = 0x8006; - -// End Device Announce and End Device Announce Response -/** - * Request: [transaction sequence number: 1] - * [node ID:2] [EUI64:8] [capabilities:1] - */ -export const END_DEVICE_ANNOUNCE = 0x0013; -/** - * No response is sent. - */ -export const END_DEVICE_ANNOUNCE_RESPONSE = 0x8013; - -// System Server Discovery Request / Response -// This is broadcast and only servers which have matching services respond. -// The response contains the request services that the recipient provides. -/** - * Request: [transaction sequence number: 1] [server mask:2] - */ -export const SYSTEM_SERVER_DISCOVERY_REQUEST = 0x0015; -/** - * Response: [transaction sequence number: 1] - * [status (== EMBER_ZDP_SUCCESS):1] [server mask:2] - */ -export const SYSTEM_SERVER_DISCOVERY_RESPONSE = 0x8015; - -// Parent Announce and Parent Announce Response -// This is broadcast and only servers which have matching children respond. -// The response contains the list of children that the recipient now holds. -/** - * Request: [transaction sequence number: 1] - * [number of children:1] [child EUI64:8]* - */ -export const PARENT_ANNOUNCE = 0x001F; -/** - * Response: [transaction sequence number: 1] - * [status: 1] [number of children:1] [child EUI64:8]* - */ -export const PARENT_ANNOUNCE_RESPONSE = 0x801F; - -// Find Node Cache Request / Response -// This is broadcast and only discovery servers which have the information for the device of interest, or the device of interest itself, respond. -// The requesting device can then direct any service discovery requests to the responder. -/** - * Request: [transaction sequence number: 1] - * [device of interest ID:2] [d-of-i EUI64:8] - */ -export const FIND_NODE_CACHE_REQUEST = 0x001C; -/** - * Response: [transaction sequence number: 1] - * [responder ID:2] [device of interest ID:2] [d-of-i EUI64:8] - */ -export const FIND_NODE_CACHE_RESPONSE = 0x801C; - -// End Device Bind Request / Response -/** - * Request: [transaction sequence number: 1] - * [node ID:2] [EUI64:8] [endpoint:1] [app profile ID:2] - * [input cluster count:1] [input cluster:2]* - * [output cluster count:1] [output cluster:2]* - */ -export const END_DEVICE_BIND_REQUEST = 0x0020; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const END_DEVICE_BIND_RESPONSE = 0x8020; - -// Clear All Bindings Request / Response -/** - * Request: [transaction sequence number: 1] - * [clear all bindings request EUI64 TLV:Variable] - * Clear all bindings request EUI64 TLV: - * [Count N:1][EUI64 1:8]...[EUI64 N:8] - */ -export const CLEAR_ALL_BINDINGS_REQUEST = 0x002B; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const CLEAR_ALL_BINDINGS_RESPONSE = 0x802B; - - -// Binding types and Request / Response -// Bind and unbind have the same formats. -// There are two possible formats, depending on whether the destination is a group address or a device address. -// Device addresses include an endpoint, groups don't. -/** - * - */ -export const UNICAST_BINDING = 0x03; -/** - * - */ -export const UNICAST_MANY_TO_ONE_BINDING = 0x83; -/** - * - */ -export const MULTICAST_BINDING = 0x01; - -/** - * Request: [transaction sequence number: 1] - * [source EUI64:8] [source endpoint:1] - * [cluster ID:2] [destination address:3 or 10] - * Destination address: - * [0x01:1] [destination group:2] - * Or: - * [0x03:1] [destination EUI64:8] [destination endpoint:1] - * - */ -export const BIND_REQUEST = 0x0021; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const BIND_RESPONSE = 0x8021; -/** - * Request: [transaction sequence number: 1] - * [source EUI64:8] [source endpoint:1] - * [cluster ID:2] [destination address:3 or 10] - * Destination address: - * [0x01:1] [destination group:2] - * Or: - * [0x03:1] [destination EUI64:8] [destination endpoint:1] - * - */ -export const UNBIND_REQUEST = 0x0022; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const UNBIND_RESPONSE = 0x8022; - - -// LQI Table Request / Response -/** - * Request: [transaction sequence number: 1] [start index:1] - */ -export const LQI_TABLE_REQUEST = 0x0031; -/** - * Response: [transaction sequence number: 1] [status:1] - * [neighbor table entries:1] [start index:1] - * [entry count:1] [entry:22]* - * [entry] = [extended PAN ID:8] [EUI64:8] [node ID:2] - * [device type, RX on when idle, relationship:1] - * [permit joining:1] [depth:1] [LQI:1] - * - * The device-type byte has the following fields: - * - * Name Mask Values - * - * device type 0x03 0x00 coordinator - * 0x01 router - * 0x02 end device - * 0x03 unknown - * - * rx mode 0x0C 0x00 off when idle - * 0x04 on when idle - * 0x08 unknown - * - * relationship 0x70 0x00 parent - * 0x10 child - * 0x20 sibling - * 0x30 other - * 0x40 previous child - * reserved 0x10 - * - * The permit-joining byte has the following fields - * - * Name Mask Values - * - * permit joining 0x03 0x00 not accepting join requests - * 0x01 accepting join requests - * 0x02 unknown - * reserved 0xFC - * - */ -export const LQI_TABLE_RESPONSE = 0x8031; - -// Routing Table Request / Response -/** - * Request: [transaction sequence number: 1] [start index:1] - */ -export const ROUTING_TABLE_REQUEST = 0x0032; -/** - * Response: [transaction sequence number: 1] [status:1] - * [routing table entries:1] [start index:1] - * [entry count:1] [entry:5]* - * [entry] = [destination address:2] - * [status:1] - * [next hop:2] - * - * - * The status byte has the following fields: - * Name Mask Values - * - * status 0x07 0x00 active - * 0x01 discovery underway - * 0x02 discovery failed - * 0x03 inactive - * 0x04 validation underway - * - * flags 0x38 - * 0x08 memory constrained - * 0x10 many-to-one - * 0x20 route record required - * - * reserved 0xC0 - */ -export const ROUTING_TABLE_RESPONSE = 0x8032; - -// Binding Table Request / Response -/** - * Request: [transaction sequence number: 1] [start index:1] - */ -export const BINDING_TABLE_REQUEST = 0x0033; -/** - * Response: [transaction sequence number: 1] - * [status:1] [binding table entries:1] [start index:1] - * [entry count:1] [entry:14/21]* - * [entry] = [source EUI64:8] [source endpoint:1] [cluster ID:2] - * [dest addr mode:1] [dest:2/8] [dest endpoint:0/1] - * [br] - * @note If Dest. Address Mode = 0x03, then the Long Dest. Address will be - * used and Dest. endpoint will be included. If Dest. Address Mode = 0x01, - * then the Short Dest. Address will be used and there will be no Dest. - * endpoint. - */ -export const BINDING_TABLE_RESPONSE = 0x8033; - -// Leave Request / Response -/** - * Request: [transaction sequence number: 1] [EUI64:8] [flags:1] - * The flag bits are: - * 0x40 remove children - * 0x80 rejoin - */ -export const LEAVE_REQUEST = 0x0034; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const LEAVE_RESPONSE = 0x8034; - -// Permit Joining Request / Response -/** - * Request: [transaction sequence number: 1] - * [duration:1] [permit authentication:1] - */ -export const PERMIT_JOINING_REQUEST = 0x0036; -/** - * Response: [transaction sequence number: 1] [status:1] - */ -export const PERMIT_JOINING_RESPONSE = 0x8036; - -// Network Update Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [scan channels:4] [duration:1] [count:0/1] [manager:0/2] - * - * If the duration is in 0x00 ... 0x05, 'count' is present but - * not 'manager'. Perform 'count' scans of the given duration on the - * given channels. - * - * If duration is 0xFE, 'channels' should have a single channel - * and 'count' and 'manager' are not present. Switch to the indicated - * channel. - * - * If duration is 0xFF, 'count' is not present. Set the active - * channels and the network manager ID to the values given. - * - * Unicast requests always get a response, which is INVALID_REQUEST if the - * duration is not a legal value. - */ -export const NWK_UPDATE_REQUEST = 0x0038; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * [scanned channels:4] [transmissions:2] [failures:2] - * [energy count:1] [energy:1]* - */ -export const NWK_UPDATE_RESPONSE = 0x8038; - -/** - * - */ -export const NWK_UPDATE_ENHANCED_REQUEST = 0x0039; -/** - * - */ -export const NWK_UPDATE_ENHANCED_RESPONSE = 0x8039; - -/** - * - */ -export const NWK_UPDATE_IEEE_JOINING_LIST_REQUEST = 0x003A; -/** - * - */ -export const NWK_UPDATE_IEEE_JOINING_LIST_REPONSE = 0x803A; - -/** - * - */ -export const NWK_UNSOLICITED_ENHANCED_UPDATE_NOTIFY = 0x803B; - - -// Beacon Survey Request / Response -// This command can be used by a remote device to survey the end devices to determine how many potential parents they have access to. -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one Beacon Survey Configuration TLV (variable octets), - * which contain the ScanChannelListStructure (variable length) - * and the ConfigurationBitmask (1 octet). This information provides - * the configuration for the end device's beacon survey. - * See R23 spec section 2.4.3.3.12 for the request and 3.2.2.2.1 - * for the ChannelListStructure. - */ -export const BEACON_SURVEY_REQUEST = 0x003C; -/** - * - * Response: [transaction sequence number: 1] - * [status: 1] - * [TLVs: varies] - * - * Contains one Beacon Survey Results TLV (4 octets), which contain - * the number of on-network, off-network, potential parent and total - * beacons recorded. If the device that received the request is not a - * router, a Potential Parent TLV (variable octects) will be found. This - * will contain information on the device's current parent, as well as - * any potential parents found via beacons (up to a maximum of 5). A - * Pan ID Conflict TLV can also found in the response. - * See R23 spec section 2.4.4.3.13 for the response. - */ -export const BEACON_SURVEY_RESPONSE = 0x803C; - -// Security Start Key Negotiation Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more Curve25519 Public Point TLVs (40 octets), - * which contain an EUI64 and the 32-byte Curve public point. - * See R23 spec section 2.4.3.4.1 - * - * @note This command SHALL NOT be APS encrypted regardless of - * whether sent before or after the device joins the network. - * This command SHALL be network encrypted if the device has a - * network key, i.e. it has joined the network earlier and wants - * to negotiate or renegotiate a new link key; otherwise, if it - * is used prior to joining the network, it SHALL NOT be network - * encrypted. - */ -export const KEY_NEGOTIATION_REQUEST = 0x0040; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * [TLVs: varies] - * - * Contains one or more Curve25519 Public Point TLVs (40 octets), - * which contain an EUI64 and the 32-byte Curve public point, or - * Local TLVs. - * See R23 spec section 2.4.4.4.1 - * - * @note This command SHALL NOT be APS encrypted. When performing - * Key Negotiation with an unauthenticated neighbor that is not - * yet on the network, network layer encryption SHALL NOT be used - * on the message. If the message is being sent to unauthenticated - * device that is not on the network and is not a neighbor, it - * SHALL be relayed as described in section 4.6.3.7.7. Otherwise - * the message SHALL have network layer encryption. - */ -export const KEY_NEGOTIATION_RESPONSE = 0x8040; - -// Retrieve Authentication Token Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more Authentication Token ID TLVs (1 octet), - * which contain the TLV Type Tag ID of the source of the - * authentication token. See R23 spec section 2.4.3.4.2 - */ -export const AUTHENTICATION_TOKEN_REQUEST = 0x0041; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * [TLVs: varies] - * - * Contains one or more 128-bit Symmetric Passphrase Global TLVs - * (16 octets), which contain the symmetric passphrase authentication - * token. See R23 spec section 2.4.4.4.2 - */ -export const AUTHENTICATION_TOKEN_RESPONSE = 0x8041; - -// Retrieve Authentication Level Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more Target IEEE Address TLVs (8 octets), - * which contain the EUI64 of the device of interest. - * See R23 spec section 2.4.3.4.3 - */ -export const AUTHENTICATION_LEVEL_REQUEST = 0x0042; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * [TLVs: varies] - * - * Contains one or more Device Authentication Level TLVs - * (10 octets), which contain the EUI64 of the inquired device, - * along with the its initial join method and its active link - * key update method. - * See R23 spec section 2.4.4.4.3 - */ -export const AUTHENTICATION_LEVEL_RESPONSE = 0x8042; - -// Set Configuration Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more Global TLVs (1 octet), - * which contain the TLV Type Tag ID, and their - * value. - */ -export const SET_CONFIGURATION_REQUEST = 0x0043; -/** - * - * Response: [transaction sequence number: 1] [status:1] - */ -export const SET_CONFIGURATION_RESPONSE = 0x8043; - -// Get Configuration Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more TLVs (1 octet), - * which the sender wants to get information - */ -export const GET_CONFIGURATION_REQUEST = 0x0044; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * [TLVs: varies] - * - * Contains one or more TLV tag Ids and their values - * in response to the request - */ -export const GET_CONFIGURATION_RESPONSE = 0x8044; - -// Security Start Key Update Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains one or more TLVs. These TLVs can be Selected Key - * Negotiation Method TLVs (10 octets), Fragmentation Parameters - * Global TLVs (5 octets), or other TLVs. - * See R23 spec section 2.4.3.4.6 - * - * @note This SHALL NOT be APS encrypted or NWK encrypted if the - * link key update mechanism is done as part of the initial join - * and before the receiving device has been issued a network - * key. This SHALL be both APS encrypted and NWK encrypted if - * the link key update mechanism is performed to refresh the - * link key when the receiving device has the network key and - * has previously successfully joined the network. - */ -export const KEY_UPDATE_REQUEST = 0x0045; -/** - * - * Response: [transaction sequence number: 1] [status:1] - * - * See R23 spec section 2.4.4.4.6 - * - * @note This command SHALL be APS encrypted. - */ -export const KEY_UPDATE_RESPONSE = 0x8045; - -// Security Decommission Request / Response -/** - * - * Request: [transaction sequence number: 1] - * [security decommission request EUI64 TLV:Variable] - * Security Decommission request EUI64 TLV: - * [Count N:1][EUI64 1:8]...[EUI64 N:8] - */ -export const SECURITY_DECOMMISSION_REQUEST = 0x0046; -/** - * - * Response: [transaction sequence number: 1] [status:1] - */ -export const SECURITY_DECOMMISSION_RESPONSE = 0x8046; - -// Challenge for APS frame counter synchronization -/** - * - * Request: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains at least the APS Frame Counter Challenge TLV, which holds the - * sender EUI and the 64 bit challenge value. - */ -export const SECURITY_CHALLENGE_REQUEST = 0x0047; -/** - * - * Response: [transaction sequence number: 1] - * [TLVs: varies] - * - * Contains at least the APS Frame Counter Response TLV, which holds the - * sender EUI, received challenge value, APS frame counter, challenge - * security frame counter, and 8-byte MIC. - */ -export const SECURITY_CHALLENGE_RESPONSE = 0x8047; - -// Unsupported Not mandatory and not supported. -/** - * - */ -export const COMPLEX_DESCRIPTOR_REQUEST = 0x0010; -/** - * - */ -export const COMPLEX_DESCRIPTOR_RESPONSE = 0x8010; -/** - * - */ -export const USER_DESCRIPTOR_REQUEST = 0x0011; -/** - * - */ -export const USER_DESCRIPTOR_RESPONSE = 0x8011; -/** - * - */ -export const DISCOVERY_REGISTER_REQUEST = 0x0012; -/** - * - */ -export const DISCOVERY_REGISTER_RESPONSE = 0x8012; -/** - * - */ -export const USER_DESCRIPTOR_SET = 0x0014; -/** - * - */ -export const USER_DESCRIPTOR_CONFIRM = 0x8014; -/** - * - */ -export const NETWORK_DISCOVERY_REQUEST = 0x0030; -/** - * - */ -export const NETWORK_DISCOVERY_RESPONSE = 0x8030; -/** - * - */ -export const DIRECT_JOIN_REQUEST = 0x0035; -/** - * - */ -export const DIRECT_JOIN_RESPONSE = 0x8035; - - -// Discovery Cache Request / Response -// DEPRECATED -/** - * Response: [transaction sequence number: 1] - * [status (== EMBER_ZDP_SUCCESS):1] - */ -export const DISCOVERY_CACHE_REQUEST = 0x0012; -/** - * Request: [transaction sequence number: 1] - * [source node ID:2] [source EUI64:8] - */ -export const DISCOVERY_CACHE_RESPONSE = 0x8012; - - -export const CLUSTER_ID_RESPONSE_MINIMUM = 0x8000; - From e4004f9a32cbe48156a9d491894a89e4b3d9df75 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Sun, 23 Jun 2024 18:58:25 +0200 Subject: [PATCH 13/17] Move to Zdo spec for request building. --- src/adapter/ember/adapter/emberAdapter.ts | 816 ++++------------------ src/adapter/ember/enums.ts | 12 - src/adapter/ember/ezsp/ezsp.ts | 13 +- 3 files changed, 152 insertions(+), 689 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index f5439d59e5..c1bd4790c0 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -16,6 +16,7 @@ import { DeviceJoinedPayload, DeviceLeavePayload, Events, + NetworkAddressPayload, ZclPayload } from '../../events'; import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from '../utils/math'; @@ -44,7 +45,6 @@ import { EmberNodeType, EmberNetworkStatus, SecManKeyType, - EmberLeaveRequestFlags, EmberInterpanMessageType, EmberSourceRouteDiscoveryMode, EmberTXPowerMode, @@ -103,6 +103,7 @@ import {EmberOneWaitress, OneWaitressEvents} from './oneWaitress'; import {logger} from '../../../utils/logger'; import {EUI64, ExtendedPanId, NodeId, PanId} from '../../../zspec/tstypes'; import {EzspError} from '../ezspError'; +import {BuffaloZdo} from '../../../zspec/zdo/buffaloZdo'; // import {EmberTokensManager} from './tokensManager'; const NS = 'zh:ember'; @@ -322,8 +323,6 @@ export class EmberAdapter extends Adapter { /** Periodically retrieve counters then clear them. */ private watchdogCountersHandle: NodeJS.Timeout; - /** Hold ZDO request in process. */ - private readonly zdoRequestBuffalo: EzspBuffalo; /** Sequence number used for ZDO requests. static uint8_t */ private zdoRequestSequence: number; /** Default radius used for broadcast ZDO requests. uint8_t */ @@ -349,7 +348,6 @@ export class EmberAdapter extends Adapter { this.requestQueue = new EmberRequestQueue(delay); this.oneWaitress = new EmberOneWaitress(); - this.zdoRequestBuffalo = new EzspBuffalo(Buffer.alloc(EZSP_MAX_FRAME_LENGTH)); this.ezsp = new Ezsp(delay, serialPortOptions); @@ -605,12 +603,26 @@ export class EmberAdapter extends Adapter { /** * Emitted from @see Ezsp.ezspIncomingMessageHandler * - * @param clusterId The ZDO response cluster ID. + * @param apsFrame The APS frame associated with the response. * @param sender The sender of the response. Should match `payload.nodeId` in many responses. - * @param payload If ZdoStatusError, the response indicated a failure. + * @param messageContents The content of the response. */ - private async onZDOResponse(sender: NodeId, apsFrame: EmberApsFrame, payload: unknown | Zdo.StatusError): Promise { - this.oneWaitress.resolveZDO(sender, apsFrame, payload); + private async onZDOResponse(apsFrame: EmberApsFrame, sender: NodeId, messageContents: Buffer): Promise { + try { + const payload = BuffaloZdo.readResponse(apsFrame.clusterId, messageContents); + + logger.debug(`<~~~ [ZDO ${Zdo.ClusterId[apsFrame.clusterId]} from=${sender} ${payload ? JSON.stringify(payload) : 'OK'}]`, NS); + this.oneWaitress.resolveZDO(sender, apsFrame, payload); + + if (apsFrame.clusterId === Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE) { + this.emit(Events.networkAddress, { + networkAddress: (payload as ZdoTypes.NetworkAddressResponse).nwkAddress, + ieeeAddr: (payload as ZdoTypes.NetworkAddressResponse).eui64 + } as NetworkAddressPayload); + } + } catch (error) { + this.oneWaitress.resolveZDO(sender, apsFrame, error); + } } /** @@ -812,7 +824,6 @@ export class EmberAdapter extends Adapter { clearInterval(this.watchdogCountersHandle); - this.zdoRequestBuffalo.setPosition(0); this.zdoRequestSequence = 0;// start at 1 this.zdoRequestRadius = 255; @@ -1848,7 +1859,14 @@ export class EmberAdapter extends Adapter { if (broadcastMgmtPermitJoin) { // `authentication`: TC significance always 1 (zb specs) - [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(ZSpec.BroadcastAddress.DEFAULT, duration, 1, DEFAULT_APS_OPTIONS)); + const zdoPayload = BuffaloZdo.buildPermitJoining(duration, 1, []); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + [status, apsFrame, messageTag] = await this.sendZDORequest( + ZSpec.BroadcastAddress.DEFAULT, + Zdo.ClusterId.PERMIT_JOINING_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS, + ); } return [status, apsFrame, messageTag]; @@ -1916,21 +1934,20 @@ export class EmberAdapter extends Adapter { * * @param destination * @param clusterId uint16_t + * @param messageContents Content of the ZDO request (sequence to be assigned at index zero) * @param options - * @param length uint8_t * @returns status Indicates success or failure (with reason) of send * @returns apsFrame The APS Frame resulting of the request being built and sent (`sequence` set from stack-given value). * @returns messageTag The tag passed to ezspSend${x} function. */ - private async sendZDORequestBuffer(destination: NodeId, clusterId: number, options: EmberApsOption): + private async sendZDORequest(destination: NodeId, clusterId: number, messageContents: Buffer, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - if (this.zdoRequestBuffalo.getPosition() > EZSP_MAX_FRAME_LENGTH) { + if (messageContents.length > EZSP_MAX_FRAME_LENGTH) { return [SLStatus.MESSAGE_TOO_LONG, null, null]; } const messageTag = this.nextZDORequestSequence(); - - this.zdoRequestBuffalo.setCommandByte(0, messageTag); + messageContents[0] = messageTag; const apsFrame: EmberApsFrame = { profileId: Zdo.ZDO_PROFILE_ID, @@ -1941,12 +1958,12 @@ export class EmberAdapter extends Adapter { groupId: 0, sequence: 0,// set by stack }; - const messageContents = this.zdoRequestBuffalo.getWritten(); if (destination === ZSpec.BroadcastAddress.DEFAULT || destination === ZSpec.BroadcastAddress.RX_ON_WHEN_IDLE || destination === ZSpec.BroadcastAddress.SLEEPY) { - logger.debug(`~~~> [ZDO BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS); - const [status, apsSequence] = (await this.ezsp.ezspSendBroadcast( + logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} BROADCAST to=${destination} messageTag=${messageTag} ` + + `messageContents=${messageContents.toString('hex')}]`, NS); + const [status, apsSequence] = await this.ezsp.ezspSendBroadcast( ZSpec.NULL_NODE_ID,// alias destination, 0,// nwkSequence @@ -1954,612 +1971,28 @@ export class EmberAdapter extends Adapter { this.getZDORequestRadius(), messageTag, messageContents, - )); + ); apsFrame.sequence = apsSequence; - logger.debug( - `~~~> [SENT ZDO type=BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${SLStatus[status]}]`, - NS, - ); + logger.debug(`~~~> [SENT ZDO type=BROADCAST apsSequence=${apsSequence} messageTag=${messageTag} status=${SLStatus[status]}`, NS); return [status, apsFrame, messageTag]; } else { - logger.debug(`~~~> [ZDO UNICAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`, NS); - const [status, apsSequence] = (await this.ezsp.ezspSendUnicast( + logger.debug(`~~~> [ZDO ${Zdo.ClusterId[clusterId]} UNICAST to=${destination} messageTag=${messageTag} ` + + `messageContents=${messageContents.toString('hex')}]`, NS); + const [status, apsSequence] = await this.ezsp.ezspSendUnicast( EmberOutgoingMessageType.DIRECT, destination, apsFrame, messageTag, messageContents, - )); + ); apsFrame.sequence = apsSequence; - logger.debug( - `~~~> [SENT ZDO type=DIRECT apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${SLStatus[status]}]`, - NS, - ); + logger.debug(`~~~> [SENT ZDO type=DIRECT apsSequence=${apsSequence} messageTag=${messageTag} status=${SLStatus[status]}`, NS); return [status, apsFrame, messageTag]; } } - /** - * ZDO - * Service Discovery Functions - * Request the specified node to send a list of its endpoints that - * match the specified application profile and, optionally, lists of input - * and/or output clusters. - * @param target The node whose matching endpoints are desired. The request can - * be sent unicast or broadcast ONLY to the "RX-on-when-idle-address" (0xFFFD) - * If sent as a broadcast, any node that has matching endpoints will send a - * response. - * @param profile uint16_t The application profile to match. - * @param inCount uint8_t The number of input clusters. To not match any input - * clusters, set this value to 0. - * @param outCount uint8_t The number of output clusters. To not match any output - * clusters, set this value to 0. - * @param inClusters uint16_t * The list of input clusters. - * @param outClusters uint16_t * The list of output clusters. - * @param options The options to use when sending the unicast request. See - * emberSendUnicast() for a description. This parameter is ignored if the target - * is a broadcast address. - * @returns An SLStatus value. EMBER_SUCCESS, MESSAGE_TOO_LONG, - * EMBER_NETWORK_DOWN or EMBER_NETWORK_BUSY. - */ - private async emberMatchDescriptorsRequest(target: NodeId, profile: number, inClusters: number[], outClusters: number[], - options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - // 2 bytes for NWK Address + 2 bytes for Profile Id + 1 byte for in Cluster Count - // + in times 2 for 2 byte Clusters + out Cluster Count + out times 2 for 2 byte Clusters - const length = (Zdo.ZDO_MESSAGE_OVERHEAD + 2 + 2 + 1 + (inClusters.length * 2) + 1 + (outClusters.length * 2)); - - // sanity check - if (length > EZSP_MAX_FRAME_LENGTH) { - return [SLStatus.MESSAGE_TOO_LONG, null, null]; - } - - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - this.zdoRequestBuffalo.writeUInt16(profile); - this.zdoRequestBuffalo.writeUInt8(inClusters.length); - this.zdoRequestBuffalo.writeListUInt16(inClusters); - this.zdoRequestBuffalo.writeUInt8(outClusters.length); - this.zdoRequestBuffalo.writeListUInt16(outClusters); - - logger.debug( - `~~~> [ZDO MATCH_DESCRIPTORS_REQUEST target=${target} profile=${profile} inClusters=${inClusters} outClusters=${outClusters}]`, - NS, - ); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.MATCH_DESCRIPTORS_REQUEST, options); - } - - /** - * ZDO - * Device Discovery Functions - * Request the 16 bit network address of a node whose EUI64 is known. - * - * @param target The EUI64 of the node. - * @param reportKids true to request that the target list their children - * in the response. - * @param childStartIndex uint8_t The index of the first child to list in the response. - * Ignored if @c reportKids is false. - * - * @return An ::SLStatus value. - * - ::EMBER_SUCCESS - The request was transmitted successfully. - * - ::EMBER_NO_BUFFERS - Insufficient message buffers were available to construct the request. - * - ::EMBER_NETWORK_DOWN - The node is not part of a network. - * - ::EMBER_NETWORK_BUSY - Transmission of the request failed. - */ - private async emberNetworkAddressRequest(target: EUI64, reportKids: boolean, childStartIndex: number) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(target); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - logger.debug(`~~~> [ZDO NETWORK_ADDRESS_REQUEST target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(ZSpec.BroadcastAddress.RX_ON_WHEN_IDLE, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, EmberApsOption.SOURCE_EUI64); - } - - /** - * ZDO - * Device Discovery Functions - * @brief Request the EUI64 of a node whose 16 bit network address is known. - * - * @param target uint16_t The network address of the node. - * @param reportKids uint8_t true to request that the target list their children - * in the response. - * @param childStartIndex uint8_t The index of the first child to list in the response. - * Ignored if reportKids is false. - * @param options The options to use when sending the request. See ::emberSendUnicast() for a description. - * - * @return An ::SLStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * - ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - */ - private async emberIeeeAddressRequest(target: NodeId, reportKids: boolean, childStartIndex: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - logger.debug(`~~~> [ZDO IEEE_ADDRESS_REQUEST target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, options); - } - - /** - * ZDO - * @param discoveryNodeId uint16_t - * @param reportKids uint8_t - * @param childStartIndex uint8_t - * @param options - * @param targetNodeIdOfRequest - */ - private async emberIeeeAddressRequestToTarget(discoveryNodeId: NodeId, reportKids: boolean, childStartIndex: number, - options: EmberApsOption, targetNodeIdOfRequest: NodeId): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(discoveryNodeId); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - logger.debug(`~~~> [ZDO IEEE_ADDRESS_REQUEST targetNodeIdOfRequest=${targetNodeIdOfRequest} discoveryNodeId=${discoveryNodeId} ` - + `reportKids=${reportKids} childStartIndex=${childStartIndex}]`, NS); - return this.sendZDORequestBuffer(targetNodeIdOfRequest, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, options); - } - - /** - * ZDO - * - * @param target uint16_t - * @param clusterId uint16_t - * @param options - * @returns - */ - private async emberSendZigDevRequestTarget(target: NodeId, clusterId: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - - return this.sendZDORequestBuffer(target, clusterId, options); - } - - /** - * ZDO - * @brief Request the specified node to send the simple descriptor for - * the specified endpoint. - * The simple descriptor contains information specific - * to a single endpoint. It describes the application profile identifier, - * application device identifier, application device version, application flags, - * application input clusters and application output clusters. It is defined in - * the ZigBee Application Framework Specification. - * - * @param target uint16_t The node of interest. - * @param targetEndpoint uint8_t The endpoint on the target node whose simple - * descriptor is desired. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberSimpleDescriptorRequest(target: NodeId, targetEndpoint: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - this.zdoRequestBuffalo.writeUInt8(targetEndpoint); - - logger.debug(`~~~> [ZDO SIMPLE_DESCRIPTOR_REQUEST target=${target} targetEndpoint=${targetEndpoint}]`, NS); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, options); - } - - /** - * ZDO - * Common logic used by `emberBindRequest` & `emberUnbindRequest`. - * - * @param target - * @param bindClusterId - * @param source - * @param sourceEndpoint - * @param clusterId - * @param type - * @param destination - * @param groupAddress - * @param destinationEndpoint - * @param options - * - * @returns An ::SLStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * - ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - * @returns APS frame created for the request - * @returns The tag used on the message. - */ - private async emberSendZigDevBindRequest(target: NodeId, bindClusterId: number, source: EUI64, sourceEndpoint: number, - clusterId: number, type: number, destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, - options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(source); - this.zdoRequestBuffalo.writeUInt8(sourceEndpoint); - this.zdoRequestBuffalo.writeUInt16(clusterId); - this.zdoRequestBuffalo.writeUInt8(type); - - switch (type) { - case Zdo.UNICAST_BINDING: - this.zdoRequestBuffalo.writeIeeeAddr(destination); - this.zdoRequestBuffalo.writeUInt8(destinationEndpoint); - break; - case Zdo.MULTICAST_BINDING: - this.zdoRequestBuffalo.writeUInt16(groupAddress); - break; - default: - return [SLStatus.FAIL, null, null]; - } - - return this.sendZDORequestBuffer(target, bindClusterId, options); - } - - /** - * ZDO - * Send a request to create a binding entry with the specified - * contents on the specified node. - * - * @param target The node on which the binding will be created. - * @param source The source EUI64 in the binding entry. - * @param sourceEndpoint The source endpoint in the binding entry. - * @param clusterId The cluster ID in the binding entry. - * @param type The type of binding, either ::UNICAST_BINDING, - * ::MULTICAST_BINDING, or ::UNICAST_MANY_TO_ONE_BINDING. - * ::UNICAST_MANY_TO_ONE_BINDING is an Ember-specific extension - * and should be used only when the target is an Ember device. - * @param destination The destination EUI64 in the binding entry for - * ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING. - * @param groupAddress The group address for the ::MULTICAST_BINDING. - * @param destinationEndpoint The destination endpoint in the binding entry for - * the ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @returns An ::SLStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * - ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - * @returns APS frame created for the request - * @returns The tag used on the message. - */ - private async emberBindRequest(target: NodeId, source: EUI64, sourceEndpoint: number, clusterId: number, type: number, - destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO BIND_REQUEST target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` - + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`, NS); - return this.emberSendZigDevBindRequest( - target, - Zdo.ClusterId.BIND_REQUEST, - source, - sourceEndpoint, - clusterId, - type, - destination, - groupAddress, - destinationEndpoint, - options - ); - } - - /** - * ZDO - * Send a request to remove a binding entry with the specified - * contents from the specified node. - * - * @param target The node on which the binding will be removed. - * @param source The source EUI64 in the binding entry. - * @param sourceEndpoint uint8_t The source endpoint in the binding entry. - * @param clusterId uint16_t The cluster ID in the binding entry. - * @param type uint8_t The type of binding, either ::UNICAST_BINDING, - * ::MULTICAST_BINDING, or ::UNICAST_MANY_TO_ONE_BINDING. - * ::UNICAST_MANY_TO_ONE_BINDING is an Ember-specific extension - * and should be used only when the target is an Ember device. - * @param destination The destination EUI64 in the binding entry for the - * ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING. - * @param groupAddress The group address for the ::MULTICAST_BINDING. - * @param destinationEndpoint uint8_t The destination endpoint in the binding entry for - * the ::UNICAST_BINDING or ::UNICAST_MANY_TO_ONE_BINDING. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @returns An ::SLStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * - ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - * @returns APS frame created for the request - * @returns The tag used on the message. - */ - private async emberUnbindRequest(target: NodeId, source: EUI64, sourceEndpoint: number, clusterId: number, type: number, - destination: EUI64, groupAddress: EmberMulticastId, destinationEndpoint: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug( - `~~~> [ZDO UNBIND_REQUEST target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` - + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`, - NS, - ); - return this.emberSendZigDevBindRequest( - target, - Zdo.ClusterId.UNBIND_REQUEST, - source, - sourceEndpoint, - clusterId, - type, - destination, - groupAddress, - destinationEndpoint, - options - ); - } - - /** - * ZDO - * Request the specified node to send a list of its active - * endpoints. An active endpoint is one for which a simple descriptor is - * available. - * - * @param target The node whose active endpoints are desired. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberActiveEndpointsRequest(target: NodeId, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO ACTIVE_ENDPOINTS_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, options); - } - - /** - * ZDO - * Request the specified node to send its power descriptor. - * The power descriptor gives a dynamic indication of the power - * status of the node. It describes current power mode, - * available power sources, current power source and - * current power source level. It is defined in the ZigBee - * Application Framework Specification. - * - * @param target The node whose power descriptor is desired. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberPowerDescriptorRequest(target: NodeId, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO POWER_DESCRIPTOR_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.POWER_DESCRIPTOR_REQUEST, options); - } - - /** - * ZDO - * Request the specified node to send its node descriptor. - * The node descriptor contains information about the capabilities of the ZigBee - * node. It describes logical type, APS flags, frequency band, MAC capabilities - * flags, manufacturer code and maximum buffer size. It is defined in the ZigBee - * Application Framework Specification. - * - * @param target The node whose node descriptor is desired. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An ::SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberNodeDescriptorRequest(target: NodeId, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO NODE_DESCRIPTOR_REQUEST target=${target}]`, NS); - return this.emberSendZigDevRequestTarget(target, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, options); - } - - /** - * ZDO - * Request the specified node to send its LQI (neighbor) table. - * The response gives PAN ID, EUI64, node ID and cost for each neighbor. The - * EUI64 is only available if security is enabled. The other fields in the - * response are set to zero. The response format is defined in the ZigBee Device - * Profile Specification. - * - * @param target The node whose LQI table is desired. - * @param startIndex uint8_t The index of the first neighbor to include in the - * response. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberLqiTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO LQI_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(Zdo.ClusterId.LQI_TABLE_REQUEST, target, startIndex, options); - } - - /** - * ZDO - * Request the specified node to send its routing table. - * The response gives destination node ID, status and many-to-one flags, - * and the next hop node ID. - * The response format is defined in the ZigBee Device - * Profile Specification. - * - * @param target The node whose routing table is desired. - * @param startIndex uint8_t The index of the first route entry to include in the - * response. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberRoutingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO ROUTING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(Zdo.ClusterId.ROUTING_TABLE_REQUEST, target, startIndex, options); - } - - /** - * ZDO - * Request the specified node to send its nonvolatile bindings. - * The response gives source address, source endpoint, cluster ID, destination - * address and destination endpoint for each binding entry. The response format - * is defined in the ZigBee Device Profile Specification. - * Note that bindings that have the Ember-specific ::UNICAST_MANY_TO_ONE_BINDING - * type are reported as having the standard ::UNICAST_BINDING type. - * - * @param target The node whose binding table is desired. - * @param startIndex uint8_t The index of the first binding entry to include in the - * response. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberBindingTableRequest(target: NodeId, startIndex: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - logger.debug(`~~~> [ZDO BINDING_TABLE_REQUEST target=${target} startIndex=${startIndex}]`, NS); - return this.emberTableRequest(Zdo.ClusterId.BINDING_TABLE_REQUEST, target, startIndex, options); - } - - /** - * ZDO - * - * @param clusterId uint16_t - * @param target - * @param startIndex uint8_t - * @param options - * @returns - */ - private async emberTableRequest(clusterId: number, target: NodeId, startIndex: number, options: EmberApsOption) - : Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt8(startIndex); - - return this.sendZDORequestBuffer(target, clusterId, options); - } - - /** - * ZDO - * Request the specified node to remove the specified device from - * the network. The device to be removed must be the node to which the request - * is sent or one of its children. - * - * @param target The node which will remove the device. - * @param deviceAddress All zeros if the target is to remove itself from - * the network or the EUI64 of a child of the target device to remove - * that child. - * @param leaveRequestFlags uint8_t A bitmask of leave options. - * Include ::AND_REJOIN if the target is to rejoin the network immediately after leaving. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberLeaveRequest(target: NodeId, deviceAddress: EUI64, leaveRequestFlags: number, options: EmberApsOption): - Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(deviceAddress); - this.zdoRequestBuffalo.writeUInt8(leaveRequestFlags); - - logger.debug(`~~~> [ZDO LEAVE_REQUEST target=${target} deviceAddress=${deviceAddress} leaveRequestFlags=${leaveRequestFlags}]`, NS); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.LEAVE_REQUEST, options); - } - - /** - * ZDO - * Request the specified node to allow or disallow association. - * - * @param target The node which will allow or disallow association. The request - * can be broadcast by using a broadcast address (0xFFFC/0xFFFD/0xFFFF). No - * response is sent if the request is broadcast. - * @param duration uint8_t A value of 0x00 disables joining. A value of 0xFF enables - * joining. Any other value enables joining for that number of seconds. - * @param authentication uint8_t Controls Trust Center authentication behavior. - * @param options The options to use when sending the request. See - * emberSendUnicast() for a description. This parameter is ignored if the target - * is a broadcast address. - * - * @return An SLStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - private async emberPermitJoiningRequest(target: NodeId, duration: number, authentication: number, options: EmberApsOption): - Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt8(duration); - this.zdoRequestBuffalo.writeUInt8(authentication); - - logger.debug(`~~~> [ZDO PERMIT_JOINING_REQUEST target=${target} duration=${duration} authentication=${authentication}]`, NS); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.PERMIT_JOINING_REQUEST, options); - } - - /** - * ZDO - * - * @see NWK_UPDATE_REQUEST - * - * @param target - * @param scanChannels uint8_t[] - * @param duration uint8_t - * @param count uint8_t - * @param manager - */ - private async emberNetworkUpdateRequest(target: NodeId, scanChannels: number[], duration: number, count: number | null, - manager: NodeId | null, options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(Zdo.ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt32(scanChannels.reduce((a, c) => a + (1 << c), 0));// to uint32_t - this.zdoRequestBuffalo.writeUInt8(duration); - - if (count != null) { - this.zdoRequestBuffalo.writeUInt8(count); - } - - if (manager != null) { - this.zdoRequestBuffalo.writeUInt16(manager); - } - - logger.debug( - `~~~> [ZDO NWK_UPDATE_REQUEST target=${target} scanChannels=${scanChannels} duration=${duration} count=${count} manager=${manager}]`, - NS, - ); - return this.sendZDORequestBuffer(target, Zdo.ClusterId.NWK_UPDATE_REQUEST, options); - } - - private async emberScanChannelsRequest(target: NodeId, scanChannels: number[], duration: number, count: number, options: EmberApsOption): - Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - return this.emberNetworkUpdateRequest(target, scanChannels, duration, count, null, options); - } - - private async emberChannelChangeRequest(target: NodeId, channel: number, options: EmberApsOption): - Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - return this.emberNetworkUpdateRequest(target, [channel], 0xFE, null, null, options); - } - - private async emberSetActiveChannelsAndNwkManagerIdRequest(target: NodeId, scanChannels: number[], manager: NodeId, - options: EmberApsOption): Promise<[SLStatus, apsFrame: EmberApsFrame, messageTag: number]> { - return this.emberNetworkUpdateRequest(target, scanChannels, 0xFF, null, manager, options); - } - //---- END Ember ZDO //-- START Adapter implementation @@ -2790,12 +2223,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); + const zdoPayload = BuffaloZdo.buildChannelChangeRequest(newChannel, null); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberChannelChangeRequest( + const [status, apsFrame, messageTag] = await this.sendZDORequest( ZSpec.BroadcastAddress.SLEEPY, - newChannel, + Zdo.ClusterId.NWK_UPDATE_REQUEST, + zdoPayload, DEFAULT_APS_OPTIONS, - )); + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed broadcast channel change to "${newChannel}" with status=${SLStatus[status]}.`, NS); @@ -2823,14 +2258,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); + const zdoPayload = BuffaloZdo.buildScanChannelsRequest(channels, duration, count); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberScanChannelsRequest( + const [status, apsFrame, messageTag] = await this.sendZDORequest( networkAddress, - channels, - duration, - count, + Zdo.ClusterId.NWK_UPDATE_REQUEST, + zdoPayload, DEFAULT_APS_OPTIONS, - )); + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed to scan channels '${channels}' on '${networkAddress} with status=${SLStatus[status]}.`, NS); @@ -3019,8 +2454,14 @@ export class EmberAdapter extends Adapter { } // `authentication`: TC significance always 1 (zb specs) + const zdoPayload = BuffaloZdo.buildPermitJoining(seconds, 1, []); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(networkAddress, seconds, 1, 0)); + const [status, apsFrame, messageTag] = await this.sendZDORequest( + networkAddress, + Zdo.ClusterId.PERMIT_JOINING_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS,// XXX: SDK has 0 here? + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed permit joining request for "${networkAddress}" with status=${SLStatus[status]}.`, NS); @@ -3097,12 +2538,18 @@ export class EmberAdapter extends Adapter { const neighbors: TsType.LQINeighbor[] = []; const request = async (startIndex: number): Promise<[SLStatus, tableEntries: number, entryCount: number]> => { + const zdoPayload = BuffaloZdo.buildLqiTableRequest(startIndex); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [reqStatus, apsFrame, messageTag] = (await this.emberLqiTableRequest(networkAddress, startIndex, DEFAULT_APS_OPTIONS)); + const [status, apsFrame, messageTag] = await this.sendZDORequest( + networkAddress, + Zdo.ClusterId.LQI_TABLE_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); - if (reqStatus !== SLStatus.OK) { - logger.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[reqStatus]}.`, NS); - return [reqStatus, null, null]; + if (status !== SLStatus.OK) { + logger.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[status]}.`, NS); + return [status, null, null]; } const result = (await this.oneWaitress.startWaitingFor({ @@ -3161,15 +2608,21 @@ export class EmberAdapter extends Adapter { const table: TsType.RoutingTableEntry[] = []; const request = async (startIndex: number): Promise<[SLStatus, tableEntries: number, entryCount: number]> => { + const zdoPayload = BuffaloZdo.buildRoutingTableRequest(startIndex); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [reqStatus, apsFrame, messageTag] = (await this.emberRoutingTableRequest(networkAddress, startIndex, DEFAULT_APS_OPTIONS)); + const [status, apsFrame, messageTag] = await this.sendZDORequest( + networkAddress, + Zdo.ClusterId.ROUTING_TABLE_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); - if (reqStatus !== SLStatus.OK) { + if (status !== SLStatus.OK) { logger.error( - `[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[reqStatus]}.`, + `[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${SLStatus[status]}.`, NS, ); - return [reqStatus, null, null]; + return [status, null, null]; } const result = (await this.oneWaitress.startWaitingFor({ @@ -3228,8 +2681,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); - /* eslint-disable @typescript-eslint/no-unused-vars */ - const [status, apsFrame, messageTag] = (await this.emberNodeDescriptorRequest(networkAddress, DEFAULT_APS_OPTIONS)); + const zdoPayload = BuffaloZdo.buildNodeDescriptorRequest(networkAddress); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = await this.sendZDORequest( + networkAddress, + Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed node descriptor for "${networkAddress}" with status=${SLStatus[status]}.`, NS); @@ -3279,8 +2738,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); + const zdoPayload = BuffaloZdo.buildActiveEndpointsRequest(networkAddress); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberActiveEndpointsRequest(networkAddress, DEFAULT_APS_OPTIONS)); + const [status, apsFrame, messageTag] = await this.sendZDORequest( + networkAddress, + Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed active endpoints request for "${networkAddress}" with status=${SLStatus[status]}.`, NS); @@ -3309,12 +2774,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); + const zdoPayload = BuffaloZdo.buildSimpleDescriptorRequest(networkAddress, endpointID); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberSimpleDescriptorRequest( + const [status, apsFrame, messageTag] = await this.sendZDORequest( networkAddress, - endpointID, + Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, + zdoPayload, DEFAULT_APS_OPTIONS - )); + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed simple descriptor request for "${networkAddress}" endpoint "${endpointID}" ` @@ -3353,18 +2820,22 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberBindRequest( - destinationNetworkAddress, + const zdoPayload = BuffaloZdo.buildBindRequest( sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, Zdo.UNICAST_BINDING, destinationAddressOrGroup as EUI64, - null,// doesn't matter - destinationEndpoint, - DEFAULT_APS_OPTIONS, - )); + undefined,// not used with UNICAST_BINDING + destinationEndpoint + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = await this.sendZDORequest( + destinationNetworkAddress, + Zdo.ClusterId.BIND_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` @@ -3391,18 +2862,22 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberBindRequest( - destinationNetworkAddress, + const zdoPayload = BuffaloZdo.buildBindRequest( sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, Zdo.MULTICAST_BINDING, - null,// doesn't matter + undefined,// not used with MULTICAST_BINDING destinationAddressOrGroup, - destinationEndpoint,// doesn't matter - DEFAULT_APS_OPTIONS, - )); + undefined// not used with MULTICAST_BINDING + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = await this.sendZDORequest( + destinationNetworkAddress, + Zdo.ClusterId.BIND_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` @@ -3436,18 +2911,22 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberUnbindRequest( - destinationNetworkAddress, + const zdoPayload = BuffaloZdo.buildUnbindRequest( sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, Zdo.UNICAST_BINDING, destinationAddressOrGroup as EUI64, - null,// doesn't matter - destinationEndpoint, - DEFAULT_APS_OPTIONS, - )); + undefined,// not used with UNICAST_BINDING + destinationEndpoint + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = await this.sendZDORequest( + destinationNetworkAddress, + Zdo.ClusterId.UNBIND_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` @@ -3475,18 +2954,22 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberUnbindRequest( - destinationNetworkAddress, + const zdoPayload = BuffaloZdo.buildUnbindRequest( sourceIeeeAddress as EUI64, sourceEndpoint, clusterID, Zdo.MULTICAST_BINDING, - null,// doesn't matter + undefined,// not used with MULTICAST_BINDING destinationAddressOrGroup, - destinationEndpoint,// doesn't matter - DEFAULT_APS_OPTIONS, - )); + undefined// not used with MULTICAST_BINDING + ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [status, apsFrame, messageTag] = await this.sendZDORequest( + destinationNetworkAddress, + Zdo.ClusterId.UNBIND_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` @@ -3517,13 +3000,14 @@ export class EmberAdapter extends Adapter { async (): Promise => { this.checkInterpanLock(); + const zdoPayload = BuffaloZdo.buildLeaveRequest(ieeeAddr as EUI64, Zdo.LeaveRequestFlags.WITHOUT_REJOIN); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberLeaveRequest( + const [status, apsFrame, messageTag] = await this.sendZDORequest( networkAddress, - ieeeAddr as EUI64, - EmberLeaveRequestFlags.WITHOUT_REJOIN, - DEFAULT_APS_OPTIONS - )); + Zdo.ClusterId.LEAVE_REQUEST, + zdoPayload, + DEFAULT_APS_OPTIONS, + ); if (status !== SLStatus.OK) { logger.error(`[ZDO] Failed remove device request for "${networkAddress}" target "${ieeeAddr}" ` @@ -3647,7 +3131,7 @@ export class EmberAdapter extends Adapter { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [status, messageTag] = (await this.ezsp.send( EmberOutgoingMessageType.MULTICAST, - apsFrame.groupId,// not used for MC + apsFrame.groupId,// not used with MULTICAST apsFrame, data, 0,// alias diff --git a/src/adapter/ember/enums.ts b/src/adapter/ember/enums.ts index 799ee95add..9e361be1a7 100644 --- a/src/adapter/ember/enums.ts +++ b/src/adapter/ember/enums.ts @@ -981,18 +981,6 @@ export enum EmberVersionType { GA = 0xAA, }; -export enum EmberLeaveRequestFlags { - /** Leave and rejoin. */ - AND_REJOIN = 0x80, - // Note: removeChildren is treated to be deprecated and should not be used! - // CCB 2047 - // - CCB makes the first step to deprecate the 'leave and remove children' functionality. - // - We were proactive here and deprecated it right away. - // AND_REMOVE_CHILDREN = 0x40, - /** Leave. */ - WITHOUT_REJOIN = 0x00, -}; - /** * For emberSetTxPowerMode and mfglibSetPower. * uint16_t diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 58adb24f25..b390173ff4 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -4,7 +4,6 @@ import {SerialPortOptions} from "../../tstype"; import {Clusters} from "../../../zspec/zcl/definition/cluster"; import * as ZSpec from "../../../zspec"; import * as Zdo from "../../../zspec/zdo"; -import {BuffaloZdo} from "../../../zspec/zdo/buffaloZdo"; import {highByte, highLowToInt, lowByte} from "../utils/math"; import { EmberOutgoingMessageType, @@ -188,7 +187,7 @@ export enum EzspEvents { NCP_NEEDS_RESET_AND_INIT = 'NCP_NEEDS_RESET_AND_INIT', //-- ezspIncomingMessageHandler - /** params => sender: NodeId, apsFrame: EmberApsFrame, payload: depends on apsFrame.clusterId */ + /** params => apsFrame: EmberApsFrame, sender: NodeId, messageContents: Buffer */ ZDO_RESPONSE = 'ZDO_RESPONSE', /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: NodeId, messageContents: Buffer */ INCOMING_MESSAGE = 'INCOMING_MESSAGE', @@ -5186,15 +5185,7 @@ export class Ezsp extends EventEmitter { ); if (apsFrame.profileId === Zdo.ZDO_PROFILE_ID) { - try { - const payload = BuffaloZdo.readResponse(apsFrame.clusterId, messageContents); - - logger.debug(`<=== [ZDO ${Zdo.ClusterId[apsFrame.clusterId]} ${payload ? JSON.stringify(payload) : ''}]`, NS); - - this.emit(EzspEvents.ZDO_RESPONSE, packetInfo.senderShortId, apsFrame, payload); - } catch (error) { - this.emit(EzspEvents.ZDO_RESPONSE, packetInfo.senderShortId, apsFrame, error); - } + this.emit(EzspEvents.ZDO_RESPONSE, apsFrame, packetInfo.senderShortId, messageContents); } else if (apsFrame.profileId === ZSpec.HA_PROFILE_ID || apsFrame.profileId === ZSpec.WILDCARD_PROFILE_ID) { this.emit(EzspEvents.INCOMING_MESSAGE, type, apsFrame, packetInfo.lastHopLqi, packetInfo.senderShortId, messageContents); } else if (apsFrame.profileId === ZSpec.GP_PROFILE_ID) { From a9fc17ebd434d889d7a5cc4738b064d216750c5f Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Mon, 24 Jun 2024 22:46:24 +0200 Subject: [PATCH 14/17] Cleanup startup sequence. Remove configs better left to firmware defaults. --- src/adapter/ember/adapter/emberAdapter.ts | 192 +++------------------- 1 file changed, 26 insertions(+), 166 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index c1bd4790c0..23f77f7907 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -47,8 +47,6 @@ import { SecManKeyType, EmberInterpanMessageType, EmberSourceRouteDiscoveryMode, - EmberTXPowerMode, - EmberKeepAliveMode, EmberJoinDecision, EmberExtendedSecurityBitmask, EmberInitialSecurityBitmask, @@ -79,7 +77,6 @@ import { EMBER_NUM_802_15_4_CHANNELS, EMBER_MIN_802_15_4_CHANNEL_NUMBER, UNKNOWN_NETWORK_STATE, - MAXIMUM_APS_PAYLOAD_LENGTH, LONG_DEST_FRAME_CONTROL, MAC_ACK_REQUIRED, MAXIMUM_INTERPAN_LENGTH, @@ -217,26 +214,6 @@ type StackConfig = { CONCENTRATOR_MAX_HOPS: number; /** <6-64> (Default: 6) @see EzspConfigId.MAX_END_DEVICE_CHILDREN */ MAX_END_DEVICE_CHILDREN: number; - /** <1-255> (Default: 10) @see EzspConfigId.APS_UNICAST_MESSAGE_COUNT */ - APS_UNICAST_MESSAGE_COUNT: number; - /** <-> (Default: 16) @see EzspConfigId.RETRY_QUEUE_SIZE */ - RETRY_QUEUE_SIZE: number; - /** <1-250> (Default: 2) @see EzspConfigId.ADDRESS_TABLE_SIZE */ - ADDRESS_TABLE_SIZE: number; - /** <0-4> (Default: 2) @see EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE */ - TRUST_CENTER_ADDRESS_CACHE_SIZE: number; - /** <0-127> (Default: 0) @see EzspConfigId.KEY_TABLE_SIZE */ - KEY_TABLE_SIZE: number; - /** <0-127> (Default: 2) @see EzspConfigId.BINDING_TABLE_SIZE */ - BINDING_TABLE_SIZE: number; - /** <15-254> (Default: 15) @see EzspConfigId.BROADCAST_TABLE_SIZE */ - BROADCAST_TABLE_SIZE: number; - /** <1-250> (Default: 8) @see EzspConfigId.MULTICAST_TABLE_SIZE */ - MULTICAST_TABLE_SIZE: number; - /** [1, 16, 26] (Default: 16). @see EzspConfigId.NEIGHBOR_TABLE_SIZE */ - NEIGHBOR_TABLE_SIZE: number; - /** <0-255> (Default: 0) @see EzspConfigId.SOURCE_ROUTE_TABLE_SIZE */ - SOURCE_ROUTE_TABLE_SIZE: number; /** <-> (Default: 10000) @see EzspValueId.TRANSIENT_DEVICE_TIMEOUT */ TRANSIENT_DEVICE_TIMEOUT: number; /** <0-14> (Default: 8) @see EzspConfigId.END_DEVICE_POLL_TIMEOUT */ @@ -261,20 +238,12 @@ const DEFAULT_STACK_CONFIG: Readonly = { CONCENTRATOR_DELIVERY_FAILURE_THRESHOLD: 1,// zigpc: 1, ZigbeeMinimalHost: 3 CONCENTRATOR_MAX_HOPS: 0,// zigpc: 0 MAX_END_DEVICE_CHILDREN: 32,// zigpc: 6, nabucasa: 32, Dongle-E (Sonoff firmware): 32 - APS_UNICAST_MESSAGE_COUNT: 32,// zigpc: 10, darkxst: 20, nabucasa: 20 - RETRY_QUEUE_SIZE: 16,// nabucasa: 16 - ADDRESS_TABLE_SIZE: 16,// zigpc: 32, darkxst: 16, nabucasa: 16 - TRUST_CENTER_ADDRESS_CACHE_SIZE: 2, - KEY_TABLE_SIZE: 0,// zigpc: 4 - BINDING_TABLE_SIZE: 32,// zigpc: 2, Z3GatewayGPCombo: 5, nabucasa: 32 - BROADCAST_TABLE_SIZE: 15,// zigpc: 15, Z3GatewayGPCombo: 35 - NOTE: Sonoff Dongle-E fails at 35 - MULTICAST_TABLE_SIZE: 16,// darkxst: 16, nabucasa: 16 - NOTE: should always be at least enough to register FIXED_ENDPOINTS multicastIds - NEIGHBOR_TABLE_SIZE: 26,// zigpc: 16, darkxst: 26, nabucasa: 26 - SOURCE_ROUTE_TABLE_SIZE: 200,// Z3GatewayGPCombo: 100, darkxst: 200, nabucasa: 200 TRANSIENT_DEVICE_TIMEOUT: 10000, END_DEVICE_POLL_TIMEOUT: 8,// zigpc: 8 TRANSIENT_KEY_TIMEOUT_S: 300,// zigpc: 65535 }; +/** Default behavior is to disable app key requests, so reduce key table to zero as well */ +const DEFAULT_KEY_TABLE_SIZE = 0; /** * NOTE: This from SDK is currently ignored here because of issues in below links: @@ -410,57 +379,6 @@ export class EmberAdapter extends Adapter { logger.error(`[STACK CONFIG] Invalid MAX_END_DEVICE_CHILDREN, using default.`, NS); } - if (!inRange(config.APS_UNICAST_MESSAGE_COUNT, 1, 255)) { - config.APS_UNICAST_MESSAGE_COUNT = DEFAULT_STACK_CONFIG.APS_UNICAST_MESSAGE_COUNT; - logger.error(`[STACK CONFIG] Invalid APS_UNICAST_MESSAGE_COUNT, using default.`, NS); - } - - if (!inRange(config.RETRY_QUEUE_SIZE, 0, 255)) { - config.RETRY_QUEUE_SIZE = DEFAULT_STACK_CONFIG.RETRY_QUEUE_SIZE; - logger.error(`[STACK CONFIG] Invalid RETRY_QUEUE_SIZE, using default.`, NS); - } - - if (!inRange(config.ADDRESS_TABLE_SIZE, 1, 250)) { - config.ADDRESS_TABLE_SIZE = DEFAULT_STACK_CONFIG.ADDRESS_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid ADDRESS_TABLE_SIZE, using default.`, NS); - } - - if (!inRange(config.TRUST_CENTER_ADDRESS_CACHE_SIZE, 0, 4)) { - config.TRUST_CENTER_ADDRESS_CACHE_SIZE = DEFAULT_STACK_CONFIG.TRUST_CENTER_ADDRESS_CACHE_SIZE; - logger.error(`[STACK CONFIG] Invalid TRUST_CENTER_ADDRESS_CACHE_SIZE, using default.`, NS); - } - - if (!inRange(config.KEY_TABLE_SIZE, 0, 127)) { - config.KEY_TABLE_SIZE = DEFAULT_STACK_CONFIG.KEY_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid KEY_TABLE_SIZE, using default.`, NS); - } - - if (!inRange(config.BINDING_TABLE_SIZE, 0, 127)) { - config.BINDING_TABLE_SIZE = DEFAULT_STACK_CONFIG.BINDING_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid BINDING_TABLE_SIZE, using default.`, NS); - } - - if (!inRange(config.BROADCAST_TABLE_SIZE, 15, 254)) { - config.BROADCAST_TABLE_SIZE = DEFAULT_STACK_CONFIG.BROADCAST_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid BROADCAST_TABLE_SIZE, using default.`, NS); - } - - // min should always be enough to cover `multicastIds` in `FIXED_ENDPOINTS` - if (!inRange(config.MULTICAST_TABLE_SIZE, 5, 250)) { - config.MULTICAST_TABLE_SIZE = DEFAULT_STACK_CONFIG.MULTICAST_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid MULTICAST_TABLE_SIZE, using default.`, NS); - } - - if (![16, 26].includes(config.NEIGHBOR_TABLE_SIZE)) { - config.NEIGHBOR_TABLE_SIZE = DEFAULT_STACK_CONFIG.NEIGHBOR_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid NEIGHBOR_TABLE_SIZE, using default.`, NS); - } - - if (!inRange(config.SOURCE_ROUTE_TABLE_SIZE, 0, 254)) { - config.SOURCE_ROUTE_TABLE_SIZE = DEFAULT_STACK_CONFIG.SOURCE_ROUTE_TABLE_SIZE; - logger.error(`[STACK CONFIG] Invalid SOURCE_ROUTE_TABLE_SIZE, using default.`, NS); - } - if (!inRange(config.TRANSIENT_DEVICE_TIMEOUT, 0, 65535)) { config.TRANSIENT_DEVICE_TIMEOUT = DEFAULT_STACK_CONFIG.TRANSIENT_DEVICE_TIMEOUT; logger.error(`[STACK CONFIG] Invalid TRANSIENT_DEVICE_TIMEOUT, using default.`, NS); @@ -853,9 +771,27 @@ export class EmberAdapter extends Adapter { // call before any other command, else fails await this.emberVersion(); - await this.initNCPPreConfiguration(); - await this.initNCPAddressTable(); - await this.initNCPConfiguration(); + /** MAC indirect timeout should be 7.68 secs (STACK_PROFILE_ZIGBEE_PRO) */ + await this.emberSetEzspConfigValue(EzspConfigId.INDIRECT_TRANSMISSION_TIMEOUT, 7680); + /** Max hops should be 2 * nwkMaxDepth, where nwkMaxDepth is 15 (STACK_PROFILE_ZIGBEE_PRO) */ + await this.emberSetEzspConfigValue(EzspConfigId.MAX_HOPS, 30); + await this.emberSetEzspConfigValue(EzspConfigId.SUPPORTED_NETWORKS, 1); + // allow other devices to modify the binding table + await this.emberSetEzspPolicy( + EzspPolicyId.BINDING_MODIFICATION_POLICY, + EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS + ); + // return message tag only in ezspMessageSentHandler() + await this.emberSetEzspPolicy(EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK); + await this.emberSetEzspValue(EzspValueId.TRANSIENT_DEVICE_TIMEOUT, 2, lowHighBytes(this.stackConfig.TRANSIENT_DEVICE_TIMEOUT)); + await this.ezsp.ezspSetManufacturerCode(this.manufacturerCode); + // network security init + await this.emberSetEzspConfigValue(EzspConfigId.STACK_PROFILE, STACK_PROFILE_ZIGBEE_PRO); + await this.emberSetEzspConfigValue(EzspConfigId.SECURITY_LEVEL, SECURITY_LEVEL_Z3); + // common configs + await this.emberSetEzspConfigValue(EzspConfigId.MAX_END_DEVICE_CHILDREN, this.stackConfig.MAX_END_DEVICE_CHILDREN); + await this.emberSetEzspConfigValue(EzspConfigId.END_DEVICE_POLL_TIMEOUT, this.stackConfig.END_DEVICE_POLL_TIMEOUT); + await this.emberSetEzspConfigValue(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, this.stackConfig.TRANSIENT_KEY_TIMEOUT_S); // WARNING: From here on EZSP commands that affect memory allocation on the NCP should no longer be called (like resizing tables) @@ -889,82 +825,6 @@ export class EmberAdapter extends Adapter { return result; } - /** - * NCP Config init. Should always be called first in the init stack (after version cmd). - * @returns - */ - private async initNCPPreConfiguration(): Promise { - // this can only decrease, not increase, NCP-side value - await this.emberSetEzspConfigValue(EzspConfigId.ADDRESS_TABLE_SIZE, this.stackConfig.ADDRESS_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE, this.stackConfig.TRUST_CENTER_ADDRESS_CACHE_SIZE); - - // BUG 14222: If stack profile is 2 (ZigBee Pro), we need to enforce - // the standard stack configuration values for that feature set. - /** MAC indirect timeout should be 7.68 secs */ - await this.emberSetEzspConfigValue(EzspConfigId.INDIRECT_TRANSMISSION_TIMEOUT, 7680); - /** Max hops should be 2 * nwkMaxDepth, where nwkMaxDepth is 15 */ - await this.emberSetEzspConfigValue(EzspConfigId.MAX_HOPS, 30); - await this.emberSetEzspConfigValue(EzspConfigId.TX_POWER_MODE, EmberTXPowerMode.USE_TOKEN); - await this.emberSetEzspConfigValue(EzspConfigId.SUPPORTED_NETWORKS, 1); - - await this.emberSetEzspValue(EzspValueId.END_DEVICE_KEEP_ALIVE_SUPPORT_MODE, 1, [EmberKeepAliveMode.KEEP_ALIVE_SUPPORT_ALL]); - - // allow other devices to modify the binding table - await this.emberSetEzspPolicy( - EzspPolicyId.BINDING_MODIFICATION_POLICY, - EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS - ); - // return message tag only in ezspMessageSentHandler() - await this.emberSetEzspPolicy(EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK); - - await this.emberSetEzspValue(EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE, 2, lowHighBytes(MAXIMUM_APS_PAYLOAD_LENGTH)); - await this.emberSetEzspValue(EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE, 2, lowHighBytes(MAXIMUM_APS_PAYLOAD_LENGTH)); - await this.emberSetEzspValue(EzspValueId.TRANSIENT_DEVICE_TIMEOUT, 2, lowHighBytes(this.stackConfig.TRANSIENT_DEVICE_TIMEOUT)); - - await this.ezsp.ezspSetManufacturerCode(this.manufacturerCode); - - // network security init - await this.emberSetEzspConfigValue(EzspConfigId.STACK_PROFILE, STACK_PROFILE_ZIGBEE_PRO); - await this.emberSetEzspConfigValue(EzspConfigId.SECURITY_LEVEL, SECURITY_LEVEL_Z3); - } - - /** - * NCP Address table init. - * @returns - */ - private async initNCPAddressTable(): Promise { - const desiredTableSize = this.stackConfig.ADDRESS_TABLE_SIZE; - // If the host and the ncp disagree on the address table size, explode. - const [status, addressTableSize] = (await this.ezsp.ezspGetConfigurationValue(EzspConfigId.ADDRESS_TABLE_SIZE)); - // After the change of ncp memory model in UC, we can not increase the default NCP table sizes anymore. - // Therefore, checking for desiredTableSize == (ncp)addressTableSize might not be always true anymore - // assert(desiredTableSize <= addressTableSize); - if ((status !== SLStatus.OK) || (addressTableSize > desiredTableSize)) { - throw new Error( - `[INIT] NCP (${addressTableSize}) disagrees with Host (min ${desiredTableSize}) on table size. status=${SLStatus[status]}` - ); - } - } - - /** - * NCP configuration init - */ - private async initNCPConfiguration(): Promise { - await this.emberSetEzspConfigValue(EzspConfigId.BINDING_TABLE_SIZE, this.stackConfig.BINDING_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.KEY_TABLE_SIZE, this.stackConfig.KEY_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.MAX_END_DEVICE_CHILDREN, this.stackConfig.MAX_END_DEVICE_CHILDREN); - await this.emberSetEzspConfigValue(EzspConfigId.APS_UNICAST_MESSAGE_COUNT, this.stackConfig.APS_UNICAST_MESSAGE_COUNT); - await this.emberSetEzspConfigValue(EzspConfigId.BROADCAST_TABLE_SIZE, this.stackConfig.BROADCAST_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.NEIGHBOR_TABLE_SIZE, this.stackConfig.NEIGHBOR_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.END_DEVICE_POLL_TIMEOUT, this.stackConfig.END_DEVICE_POLL_TIMEOUT); - // SL_ZIGBEE_EZSP_CONFIG_ZLL_GROUP_ADDRESSES 1 - // SL_ZIGBEE_EZSP_CONFIG_ZLL_RSSI_THRESHOLD -128 - await this.emberSetEzspConfigValue(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, this.stackConfig.TRANSIENT_KEY_TIMEOUT_S); - await this.emberSetEzspConfigValue(EzspConfigId.RETRY_QUEUE_SIZE, this.stackConfig.RETRY_QUEUE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.SOURCE_ROUTE_TABLE_SIZE, this.stackConfig.SOURCE_ROUTE_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.MULTICAST_TABLE_SIZE, this.stackConfig.MULTICAST_TABLE_SIZE); - } - /** * NCP concentrator init. Also enables source route discovery mode with RESCHEDULE. * @@ -1069,7 +929,7 @@ export class EmberAdapter extends Adapter { + `with status=${SLStatus[status]}.`); } - const appKeyPolicy = this.stackConfig.KEY_TABLE_SIZE ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS; + const appKeyPolicy = DEFAULT_KEY_TABLE_SIZE > 0 ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS; status = (await this.emberSetEzspPolicy(EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyPolicy)); if (status !== SLStatus.OK) { @@ -1294,7 +1154,7 @@ export class EmberAdapter extends Adapter { throw new Error(`[INIT FORM] Failed to set extended security bitmask to ${extended} with status=${SLStatus[status]}.`); } - if (!fromBackup && this.stackConfig.KEY_TABLE_SIZE > 0) { + if (!fromBackup && DEFAULT_KEY_TABLE_SIZE > 0) { status = await this.ezsp.ezspClearKeyTable(); if (status !== SLStatus.OK) { @@ -2114,7 +1974,7 @@ export class EmberAdapter extends Adapter { let keyList: LinkKeyBackupData[] = []; - if (this.stackConfig.KEY_TABLE_SIZE > 0) { + if (DEFAULT_KEY_TABLE_SIZE > 0) { keyList = (await this.exportLinkKeys()); } From 9bdba32b1a8f803f7515ec4a10587a04fd083b53 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Wed, 26 Jun 2024 03:22:49 +0200 Subject: [PATCH 15/17] Separate buffers for responses and callbacks. Improve queue behavior. --- src/adapter/ember/adapter/emberAdapter.ts | 48 +- src/adapter/ember/adapter/requestQueue.ts | 89 ++-- src/adapter/ember/adapter/tokensManager.ts | 2 +- src/adapter/ember/ezsp/buffalo.ts | 2 +- src/adapter/ember/ezsp/ezsp.ts | 592 ++++++++++----------- src/adapter/ember/uart/ash.ts | 20 +- test/adapter/ember/consts.ts | 38 ++ test/adapter/ember/ezsp.test.ts | 168 ++++-- test/adapter/ember/requestQueue.test.ts | 347 ++++++------ 9 files changed, 695 insertions(+), 611 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 23f77f7907..63572b9089 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -318,12 +318,10 @@ export class EmberAdapter extends Adapter { this.requestQueue = new EmberRequestQueue(delay); this.oneWaitress = new EmberOneWaitress(); - this.ezsp = new Ezsp(delay, serialPortOptions); + this.ezsp = new Ezsp(serialPortOptions); this.ezsp.on(EzspEvents.STACK_STATUS, this.onStackStatus.bind(this)); - this.ezsp.on(EzspEvents.MESSAGE_SENT, this.onMessageSent.bind(this)); - this.ezsp.on(EzspEvents.ZDO_RESPONSE, this.onZDOResponse.bind(this)); this.ezsp.on(EzspEvents.END_DEVICE_ANNOUNCE, this.onEndDeviceAnnounce.bind(this)); this.ezsp.on(EzspEvents.INCOMING_MESSAGE, this.onIncomingMessage.bind(this)); @@ -969,7 +967,7 @@ export class EmberAdapter extends Adapter { const [npStatus, nodeType, netParams] = (await this.ezsp.ezspGetNetworkParameters()); logger.debug(`[INIT TC] Current network config=${JSON.stringify(this.networkOptions)}`, NS); - logger.debug(`[INIT TC] Current NCP network: nodeType=${EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`, NS); + logger.debug(`[INIT TC] Current adapter network: nodeType=${EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`, NS); if ((npStatus === SLStatus.OK) && (nodeType === EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId) && (equals(this.networkOptions.extendedPanID, netParams.extendedPanId))) { @@ -983,7 +981,7 @@ export class EmberAdapter extends Adapter { throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); } - logger.debug(`[INIT TC] Current NCP network: networkKey=${networkKey.contents.toString('hex')}`, NS); + logger.debug(`[INIT TC] Current adapter network: networkKey=${networkKey.contents.toString('hex')}`, NS); // config doesn't match adapter anymore if (!networkKey.contents.equals(configNetworkKey)) { @@ -995,7 +993,7 @@ export class EmberAdapter extends Adapter { } if (action === NetworkInitAction.LEAVE) { - logger.info(`[INIT TC] NCP network does not match config. Leaving network...`, NS); + logger.info(`[INIT TC] Adapter network does not match config. Leaving network...`, NS); const leaveStatus = (await this.ezsp.ezspLeaveNetwork()); if (leaveStatus !== SLStatus.OK) { @@ -1087,7 +1085,7 @@ export class EmberAdapter extends Adapter { break; } case NetworkInitAction.DONE: { - logger.info(`[INIT TC] NCP network matches config.`, NS); + logger.info(`[INIT TC] Adapter network matches config.`, NS); break; } default: { @@ -1368,15 +1366,22 @@ export class EmberAdapter extends Adapter { * @param status */ private async onNcpNeedsResetAndInit(status: EzspStatus): Promise { - logger.error(`!!! NCP FATAL ERROR reason=${EzspStatus[status]}. ATTEMPTING RESET... !!!`, NS); + logger.error(`!!! ADAPTER FATAL ERROR reason=${EzspStatus[status]}. !!!`, NS); - try { - await this.stop(); - await Wait(500);// just because - await this.start(); - } catch (err) { - logger.error(`Failed to reset and init NCP. ${err}`, NS); + if (this.requestQueue.isHigh) { + logger.info(`Request queue is high (${this.requestQueue.totalQueued}), triggering full restart to prevent stressing the adapter.`, NS); this.emit(Events.disconnected); + } else { + logger.info(`Attempting adapter reset...`, NS); + + try { + await this.stop(); + await Wait(500);// just because + await this.start(); + } catch (err) { + logger.error(`Failed to reset and init adapter. ${err}`, NS); + this.emit(Events.disconnected); + } } } @@ -1527,19 +1532,18 @@ export class EmberAdapter extends Adapter { } if (ncpEzspProtocolVer === EZSP_PROTOCOL_VERSION) { - logger.debug(`NCP EZSP protocol version (${ncpEzspProtocolVer}) matches Host.`, NS); + logger.debug(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) matches Host.`, NS); } else if (ncpEzspProtocolVer < EZSP_PROTOCOL_VERSION && ncpEzspProtocolVer >= EZSP_MIN_PROTOCOL_VERSION) { [ncpEzspProtocolVer, ncpStackType, ncpStackVer] = await this.ezsp.ezspVersion(ncpEzspProtocolVer); - logger.info(`NCP EZSP protocol version (${ncpEzspProtocolVer}) lower than Host. Switched.`, NS); + logger.info(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) lower than Host. Switched.`, NS); } else { - throw new Error( - `NCP EZSP protocol version (${ncpEzspProtocolVer}) is not supported by Host [${EZSP_MIN_PROTOCOL_VERSION}-${EZSP_PROTOCOL_VERSION}].` - ); + throw new Error(`Adapter EZSP protocol version (${ncpEzspProtocolVer}) is not supported ` + + `by Host [${EZSP_MIN_PROTOCOL_VERSION}-${EZSP_PROTOCOL_VERSION}].`); } this.ezsp.setProtocolVersion(ncpEzspProtocolVer); - logger.debug(`NCP info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`, NS); + logger.debug(`Adapter info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`, NS); const [status, versionStruct] = (await this.ezsp.ezspGetVersionStruct()); @@ -1555,10 +1559,10 @@ export class EmberAdapter extends Adapter { }; if (versionStruct.type !== EmberVersionType.GA) { - logger.warning(`NCP is running a non-GA version (${EmberVersionType[versionStruct.type]}).`, NS); + logger.warning(`Adapter is running a non-GA version (${EmberVersionType[versionStruct.type]}).`, NS); } - logger.debug(`NCP version info: ${JSON.stringify(this.version)}`, NS); + logger.info(`Adapter version info: ${JSON.stringify(this.version)}`, NS); } /** diff --git a/src/adapter/ember/adapter/requestQueue.ts b/src/adapter/ember/adapter/requestQueue.ts index 8bc08c586c..1afef35eaa 100644 --- a/src/adapter/ember/adapter/requestQueue.ts +++ b/src/adapter/ember/adapter/requestQueue.ts @@ -1,4 +1,3 @@ -/* istanbul ignore file */ import {logger} from "../../../utils/logger"; import {EzspStatus, SLStatus} from "../enums"; import {EzspError} from "../ezspError"; @@ -6,10 +5,7 @@ import {EzspError} from "../ezspError"; const NS = 'zh:ember:queue'; interface EmberRequestQueueEntry { - /** - * Times tried to successfully send the call. - * This has no maximum, but since it is only for temporary issues, it will either succeed after a couple of tries, or hard fail. - */ + /** Times tried to successfully send the call. */ sendAttempts: number; /** The function the entry is supposed to execute. */ func: () => Promise; @@ -17,6 +13,8 @@ interface EmberRequestQueueEntry { reject: (reason: Error) => void; }; +export const MAX_SEND_ATTEMPTS = 3; +export const HIGH_COUNT = 4; export const BUSY_DEFER_MSEC = 500; export const NETWORK_DOWN_DEFER_MSEC = 1500; @@ -36,12 +34,28 @@ export class EmberRequestQueue { this.priorityQueue = []; } + /** + * Number of requests in both regular and priority queues. + */ + get totalQueued(): number { + return this.queue.length + this.priorityQueue.length; + } + + /** + * If true, total queued requests count is considered high. + */ + get isHigh(): boolean { + return this.totalQueued > HIGH_COUNT; + } + /** * Empty each queue. */ public clear(): void { this.queue = []; this.priorityQueue = []; + + logger.info(`Request queues cleared.`, NS); } /** @@ -79,6 +93,7 @@ export class EmberRequestQueue { */ public enqueue(func: () => Promise, reject: (reason: Error) => void, prioritize: boolean = false): number { logger.debug(`Status queue=${this.queue.length} priorityQueue=${this.priorityQueue.length}.`, NS); + return (prioritize ? this.priorityQueue : this.queue).push({ sendAttempts: 0, func, @@ -111,37 +126,41 @@ export class EmberRequestQueue { } if (entry) { - entry.sendAttempts++; - - // NOTE: refer to `enqueue()` comment to keep logic in sync with expectations, adjust comment on change. - try { - const status: SLStatus = (await entry.func()); - - // XXX: add NOT_READY? - if ((status === SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED) || (status === SLStatus.BUSY)) { - logger.debug(`Dispatching deferred: NCP busy.`, NS); - this.defer(BUSY_DEFER_MSEC); - } else if (status === SLStatus.NETWORK_DOWN) { - logger.debug(`Dispatching deferred: Network not ready`, NS); - this.defer(NETWORK_DOWN_DEFER_MSEC); - } else { - // success - (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); - - if (status !== SLStatus.OK) { - entry.reject(new Error(SLStatus[status])); + entry.sendAttempts++;// enqueued at zero + + if (entry.sendAttempts > MAX_SEND_ATTEMPTS) { + entry.reject(new Error(`Failed ${MAX_SEND_ATTEMPTS} attempts to send`)); + } else { + // NOTE: refer to `enqueue()` comment to keep logic in sync with expectations, adjust comment on change. + try { + const status: SLStatus = (await entry.func()); + + // XXX: add NOT_READY? + if ((status === SLStatus.ZIGBEE_MAX_MESSAGE_LIMIT_REACHED) || (status === SLStatus.BUSY)) { + logger.debug(`Dispatching deferred: Adapter busy.`, NS); + this.defer(BUSY_DEFER_MSEC); + } else if (status === SLStatus.NETWORK_DOWN) { + logger.debug(`Dispatching deferred: Network not ready`, NS); + this.defer(NETWORK_DOWN_DEFER_MSEC); + } else { + // success + (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); + + if (status !== SLStatus.OK) { + entry.reject(new Error(SLStatus[status])); + } + } + } catch (err) {// EzspStatusError from ezsp${x} commands, except for stuff rejected by OneWaitress, but that's never "retry" + if ((err as EzspError).code === EzspStatus.NO_TX_SPACE) { + logger.debug(`Dispatching deferred: Host busy.`, NS); + this.defer(BUSY_DEFER_MSEC); + } else if ((err as EzspError).code === EzspStatus.NOT_CONNECTED) { + logger.debug(`Dispatching deferred: Network not ready`, NS); + this.defer(NETWORK_DOWN_DEFER_MSEC); + } else { + (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); + entry.reject(err); } - } - } catch (err) {// EzspStatusError from ezsp${x} commands, except for stuff rejected by OneWaitress, but that's never "retry" - if ((err as EzspError).code === EzspStatus.NO_TX_SPACE) { - logger.debug(`Dispatching deferred: Host busy.`, NS); - this.defer(BUSY_DEFER_MSEC); - } else if ((err as EzspError).code === EzspStatus.NOT_CONNECTED) { - logger.debug(`Dispatching deferred: Network not ready`, NS); - this.defer(NETWORK_DOWN_DEFER_MSEC); - } else { - (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); - entry.reject(err); } } } diff --git a/src/adapter/ember/adapter/tokensManager.ts b/src/adapter/ember/adapter/tokensManager.ts index fccb650f3d..6735251c65 100644 --- a/src/adapter/ember/adapter/tokensManager.ts +++ b/src/adapter/ember/adapter/tokensManager.ts @@ -410,7 +410,7 @@ export class EmberTokensManager { } else { // ezspGetTokenCount == 0 OR (ezspGetTokenInfo|ezspGetTokenData|ezspSetTokenData return LIBRARY_NOT_PRESENT) // ezspTokenFactoryReset will do nothing. - logger.error(`[TOKENS] Saving tokens not supported by NCP (not NVM3-based).`, NS); + logger.error(`[TOKENS] Saving tokens not supported by adapter (not NVM3-based).`, NS); } return null; diff --git a/src/adapter/ember/ezsp/buffalo.ts b/src/adapter/ember/ezsp/buffalo.ts index cbefb89977..74c1cf2c3e 100644 --- a/src/adapter/ember/ezsp/buffalo.ts +++ b/src/adapter/ember/ezsp/buffalo.ts @@ -1375,7 +1375,7 @@ export class EzspBuffalo extends Buffalo { const bindingIndex = this.readUInt8(); const addressIndex = this.readUInt8(); const lastHopLqi = this.readUInt8(); - const lastHopRssi = this.readInt8(); + const lastHopRssi = this.readInt8();// SDK: (int8_t)fetchInt8u(); const lastHopTimestamp = this.readUInt32(); return { diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index b390173ff4..8cd9c482a6 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -236,6 +236,11 @@ export class Ezsp extends EventEmitter { private readonly frameContents: Buffer; /** The total Length of the incoming frame */ private frameLength: number; + private readonly callbackBuffalo: EzspBuffalo; + /** The contents of the current EZSP frame. CAREFUL using this guy, it's pre-allocated. */ + private readonly callbackFrameContents: Buffer; + /** The total Length of the incoming frame */ + private callbackFrameLength: number; private initialVersionSent: boolean; /** True if a command is in the process of being sent. */ @@ -252,18 +257,15 @@ export class Ezsp extends EventEmitter { /** Counter for Queue Full errors */ public counterErrQueueFull: number; - /** Handle used to tick for possible received callbacks */ - private tickHandle: NodeJS.Timeout; - - constructor(tickInterval: number, options: SerialPortOptions) { + constructor(options: SerialPortOptions) { super(); - this.tickInterval = tickInterval || 5; this.frameContents = Buffer.alloc(EZSP_MAX_FRAME_LENGTH); this.buffalo = new EzspBuffalo(this.frameContents); + this.callbackFrameContents = Buffer.alloc(EZSP_MAX_FRAME_LENGTH); + this.callbackBuffalo = new EzspBuffalo(this.callbackFrameContents); this.ash = new UartAsh(options); - this.ash.on(AshEvents.FRAME, this.onAshFrame.bind(this)); } /** @@ -283,17 +285,28 @@ export class Ezsp extends EventEmitter { return `[FRAME: ID=${id}:"${EzspFrameID[id]}" Seq=${this.frameContents[EZSP_SEQUENCE_INDEX]} Len=${this.frameLength}]`; } + /** + * Create a string representation of the last callback frame in storage (received only). + */ + get callbackFrameToString(): string { + const id = this.callbackBuffalo.getFrameId(); + return `[CBFRAME: ID=${id}:"${EzspFrameID[id]}" Seq=${this.callbackFrameContents[EZSP_SEQUENCE_INDEX]} Len=${this.callbackFrameLength}]`; + } + private initVariables(): void { if (this.waitingForResponse) { clearTimeout(this.responseWaiter.timer); } - clearTimeout(this.tickHandle); this.ash.removeAllListeners(AshEvents.FATAL_ERROR); + this.ash.removeAllListeners(AshEvents.FRAME); this.frameContents.fill(0); this.frameLength = 0; this.buffalo.setPosition(0); + this.callbackFrameContents.fill(0); + this.callbackFrameLength = 0; + this.callbackBuffalo.setPosition(0); this.initialVersionSent = false; this.sendingCommand = false; this.frameSequence = -1;// start at 0 @@ -301,7 +314,6 @@ export class Ezsp extends EventEmitter { this.waitingForResponse = false; this.responseWaiter = null; this.counterErrQueueFull = 0; - this.tickHandle = null; } public async start(): Promise { @@ -323,9 +335,10 @@ export class Ezsp extends EventEmitter { if (status === EzspStatus.SUCCESS) { logger.info(`======== EZSP started ========`, NS); - // registered after reset sequence to avoid bubbling up to adapter before this point + // registered after reset sequence + this.ash.on(AshEvents.FRAME, this.onAshFrame.bind(this)); this.ash.on(AshEvents.FATAL_ERROR, this.onAshFatalError.bind(this)); - this.tick(); + return status; } } @@ -362,20 +375,61 @@ export class Ezsp extends EventEmitter { return this.ash.connected; } + /** + * Triggered by @see AshEvents.FATAL_ERROR + */ private onAshFatalError(status: EzspStatus): void { this.emit(EzspEvents.NCP_NEEDS_RESET_AND_INIT, status); } + /** + * Triggered by @see AshEvents.FRAME + */ private onAshFrame(): void { - // let tick handle if not waiting for response (CBs) - if (this.waitingForResponse) { - const status = this.responseReceived(); + // trigger housekeeping in ASH layer + this.ash.sendExec(); + + const buffer: EzspBuffer = this.ash.rxQueue.getPrecedingEntry(null); + + if (buffer == null) { + // something is seriously wrong + logger.error(`Found no buffer in queue but ASH layer sent signal that one was available.`, NS); + return; + } + + this.ash.rxQueue.removeEntry(buffer); + + // logger.debug(`<<<< ${buffer.data.subarray(0, buffer.len).toString('hex')}`, NS); + + if (buffer.data[EZSP_FRAME_CONTROL_INDEX] & EZSP_FRAME_CONTROL_ASYNCH_CB) { + buffer.data.copy(this.callbackFrameContents, 0, 0, buffer.len);// take only what len tells us is actual content + + this.callbackFrameLength = buffer.len; + + logger.debug(`<=== ${this.callbackFrameToString}`, NS); + + this.ash.rxFree.freeBuffer(buffer); - if (status !== EzspStatus.NO_RX_DATA) { - // we've got a non-CB frame, must be it! - clearTimeout(this.responseWaiter.timer); - this.responseWaiter.resolve(status); + const status = this.validateReceivedFrame(this.callbackBuffalo); + + if (status === EzspStatus.SUCCESS) { + this.callbackDispatch(); + } else { + logger.debug(`<=x= ${this.callbackFrameToString} Invalid, status=${EzspStatus[status]}.`, NS); } + } else { + buffer.data.copy(this.frameContents, 0, 0, buffer.len);// take only what len tells us is actual content + + this.frameLength = buffer.len; + + logger.debug(`<=== ${this.frameToString}`, NS); + + this.ash.rxFree.freeBuffer(buffer); + + const status = this.validateReceivedFrame(this.buffalo); + + clearTimeout(this.responseWaiter.timer); + this.responseWaiter.resolve(status); } } @@ -387,47 +441,33 @@ export class Ezsp extends EventEmitter { * @param status */ public ezspErrorHandler(status: EzspStatus): void { - const lastFrameStr = `Last: ${this.frameToString}.`; + const lastFrameStr = `Last Frame: ${this.frameToString}.`; if (status === EzspStatus.ERROR_QUEUE_FULL) { this.counterErrQueueFull += 1; - logger.error(`NCP Queue full (counter: ${this.counterErrQueueFull}). ${lastFrameStr}`, NS); + logger.error(`Adapter queue full (counter: ${this.counterErrQueueFull}). ${lastFrameStr}`, NS); } else if (status === EzspStatus.ERROR_OVERFLOW) { logger.error( - `The NCP has run out of buffers, causing general malfunction. Remediate network congestion, if present. ${lastFrameStr}`, + `The adapter has run out of buffers, causing general malfunction. Remediate network congestion, if present. ${lastFrameStr}`, NS, ); } else { logger.error(`ERROR Transaction failure; status=${EzspStatus[status]}. ${lastFrameStr}`, NS); } + // Do not reset if improper frame control direction flag (XXX: certain devices appear to trigger this somehow, without reason?) // Do not reset if this is a decryption failure, as we ignored the packet // Do not reset for a callback overflow or error queue, as we don't want the device to reboot under stress; - // Resetting under these conditions does not solve the problem as the problem is external to the NCP. + // resetting under these conditions does not solve the problem as the problem is external to the NCP. // Throttling the additional traffic and staggering things might make it better instead. // For all other errors, we reset the NCP if ((status !== EzspStatus.ERROR_SECURITY_PARAMETERS_INVALID) && (status !== EzspStatus.ERROR_OVERFLOW) - && (status !== EzspStatus.ERROR_QUEUE_FULL)) { + && (status !== EzspStatus.ERROR_QUEUE_FULL) && (status !== EzspStatus.ERROR_WRONG_DIRECTION)) { this.emit(EzspEvents.NCP_NEEDS_RESET_AND_INIT, status); } } - /** - * The Host application must call this function periodically to allow the EZSP layer to handle asynchronous events. - */ - private tick(): void { - // don't process any callbacks while sending a command and waiting for its response - // nothing in the rx queue, nothing to receive - if (!this.sendingCommand && !this.ash.rxQueue.empty) { - if (this.responseReceived() === EzspStatus.SUCCESS) { - this.callbackDispatch(); - } - } - - this.tickHandle = setTimeout(this.tick.bind(this), this.tickInterval); - } - private nextFrameSequence(): number { return (this.frameSequence = ((++this.frameSequence) & 0xFF)); } @@ -518,23 +558,16 @@ export class Ezsp extends EventEmitter { logger.debug(`===> ${this.frameToString}`, NS); try { - status = await new Promise((resolve, reject: (reason: Error) => void): void => { + status = await new Promise((resolve, reject: (reason: EzspError) => void): void => { const sendStatus = (this.ash.send(this.frameLength, this.frameContents)); if (sendStatus !== EzspStatus.SUCCESS) { reject(new EzspError(sendStatus)); } - const error = new Error(); - Error.captureStackTrace(error); - - this.waitingForResponse = true; this.responseWaiter = { timer: setTimeout(() => { - this.waitingForResponse = false; - error.message = `timed out after ${this.ash.responseTimeout}ms`; - - reject(error); + reject(new EzspError(EzspStatus.ASH_ERROR_TIMEOUTS)); }, this.ash.responseTimeout), resolve, }; @@ -543,8 +576,8 @@ export class Ezsp extends EventEmitter { if (status !== EzspStatus.SUCCESS) { throw new EzspError(status); } - } catch (err) { - logger.debug(`=x=> ${this.frameToString} Error: ${err}`, NS); + } catch (error) { + logger.debug(`=x=> ${this.frameToString} ${error}`, NS); this.ezspErrorHandler(status); } @@ -555,109 +588,45 @@ export class Ezsp extends EventEmitter { } /** - * Checks whether a new EZSP response frame has been received. - * If any, the response payload is stored in frameContents/frameLength. - * Any other return value means that an error has been detected by the serial protocol layer. - * @returns NO_RX_DATA if no new response has been received. - * @returns SUCCESS if a new response has been received. - */ - public checkResponseReceived(): EzspStatus { - // trigger housekeeping in ASH layer - this.ash.sendExec(); - - let status: EzspStatus = EzspStatus.NO_RX_DATA; - let dropBuffer: EzspBuffer = null; - let buffer: EzspBuffer = this.ash.rxQueue.getPrecedingEntry(null); - - while (buffer != null) { - // While we are waiting for a response to a command, we use the asynch callback flag to ignore asynchronous callbacks. - // This allows our caller to assume that no callbacks will appear between sending a command and receiving its response. - if (this.waitingForResponse && (buffer.data[EZSP_FRAME_CONTROL_INDEX] & EZSP_FRAME_CONTROL_ASYNCH_CB)) { - logger.debug(`Skipping async callback while waiting for response to command.`, NS); - - if (this.ash.rxFree.length === 0) { - dropBuffer = buffer; - } - - buffer = this.ash.rxQueue.getPrecedingEntry(buffer); - } else { - this.ash.rxQueue.removeEntry(buffer); - buffer.data.copy(this.frameContents, 0, 0, buffer.len);// take only what len tells us is actual content - - this.frameLength = buffer.len; - - logger.debug(`<=== ${this.frameToString}`, NS); - - this.ash.rxFree.freeBuffer(buffer); - - buffer = null; - status = EzspStatus.SUCCESS; - this.waitingForResponse = false; - } - } - - if (dropBuffer != null) { - this.ash.rxQueue.removeEntry(dropBuffer); - this.ash.rxFree.freeBuffer(dropBuffer); - - logger.debug(`ERROR Host receive queue full. Dropping received callback: ${dropBuffer.data.toString('hex')}`, NS); - - this.ezspErrorHandler(EzspStatus.ERROR_QUEUE_FULL); - } - - return status; - } - - /** - * Check if a response was received and sets the stage for parsing if valid (indexes buffalo to params index). + * Sets the stage for parsing if valid (indexes buffalo to params index). * @returns */ - public responseReceived(): EzspStatus { - let status: EzspStatus; - - status = this.checkResponseReceived(); - - if (status === EzspStatus.NO_RX_DATA) { - return status; - } - + public validateReceivedFrame(buffalo: EzspBuffalo): EzspStatus { + let status: EzspStatus = EzspStatus.SUCCESS; let frameControl: number, frameId: number, parametersIndex: number; // eslint-disable-next-line prefer-const - [status, frameControl, frameId, parametersIndex] = this.buffalo.getResponseMetadata(); - - if (status === EzspStatus.SUCCESS) { - if (frameId === EzspFrameID.INVALID_COMMAND) { - status = this.buffalo.getResponseByte(parametersIndex); - } + [status, frameControl, frameId, parametersIndex] = buffalo.getResponseMetadata(); - if ((frameControl & EZSP_FRAME_CONTROL_DIRECTION_MASK) !== EZSP_FRAME_CONTROL_RESPONSE) { - status = EzspStatus.ERROR_WRONG_DIRECTION; - } + if (frameId === EzspFrameID.INVALID_COMMAND) { + status = buffalo.getResponseByte(parametersIndex); + } - if ((frameControl & EZSP_FRAME_CONTROL_TRUNCATED_MASK) === EZSP_FRAME_CONTROL_TRUNCATED) { - status = EzspStatus.ERROR_TRUNCATED; - } + if ((frameControl & EZSP_FRAME_CONTROL_DIRECTION_MASK) !== EZSP_FRAME_CONTROL_RESPONSE) { + status = EzspStatus.ERROR_WRONG_DIRECTION; + } - if ((frameControl & EZSP_FRAME_CONTROL_OVERFLOW_MASK) === EZSP_FRAME_CONTROL_OVERFLOW) { - status = EzspStatus.ERROR_OVERFLOW; - } + if ((frameControl & EZSP_FRAME_CONTROL_TRUNCATED_MASK) === EZSP_FRAME_CONTROL_TRUNCATED) { + status = EzspStatus.ERROR_TRUNCATED; + } - if ((frameControl & EZSP_FRAME_CONTROL_PENDING_CB_MASK) === EZSP_FRAME_CONTROL_PENDING_CB) { - this.ash.ncpHasCallbacks = true; - } else { - this.ash.ncpHasCallbacks = false; - } + if ((frameControl & EZSP_FRAME_CONTROL_OVERFLOW_MASK) === EZSP_FRAME_CONTROL_OVERFLOW) { + status = EzspStatus.ERROR_OVERFLOW; + } - // Set the callback network - //this.callbackNetworkIndex = (frameControl & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK) >> EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET; + if ((frameControl & EZSP_FRAME_CONTROL_PENDING_CB_MASK) === EZSP_FRAME_CONTROL_PENDING_CB) { + this.ash.ncpHasCallbacks = true; + } else { + this.ash.ncpHasCallbacks = false; } + // Set the callback network + //this.callbackNetworkIndex = (frameControl & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK) >> EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET; + if (status !== EzspStatus.SUCCESS) { - logger.debug(`[RESPONSE RECEIVED] ERROR ${EzspStatus[status]}`, NS); this.ezspErrorHandler(status); } - this.buffalo.setPosition(parametersIndex); + buffalo.setPosition(parametersIndex); // An overflow does not indicate a comms failure; // The system can still communicate but buffers are running critically low. @@ -673,136 +642,136 @@ export class Ezsp extends EventEmitter { * Dispatches callback frames handlers. */ public callbackDispatch(): void { - switch (this.buffalo.getExtFrameId()) { + switch (this.callbackBuffalo.getExtFrameId()) { case EzspFrameID.NO_CALLBACKS: { this.ezspNoCallbacks(); break; } case EzspFrameID.STACK_TOKEN_CHANGED_HANDLER: { - const tokenAddress = this.buffalo.readUInt16(); + const tokenAddress = this.callbackBuffalo.readUInt16(); this.ezspStackTokenChangedHandler(tokenAddress); break; } case EzspFrameID.TIMER_HANDLER: { - const timerId = this.buffalo.readUInt8(); + const timerId = this.callbackBuffalo.readUInt8(); this.ezspTimerHandler(timerId); break; } case EzspFrameID.COUNTER_ROLLOVER_HANDLER: { - const type: EmberCounterType = this.buffalo.readUInt8(); + const type: EmberCounterType = this.callbackBuffalo.readUInt8(); this.ezspCounterRolloverHandler(type); break; } case EzspFrameID.CUSTOM_FRAME_HANDLER: { - const payload = this.buffalo.readPayload(); + const payload = this.callbackBuffalo.readPayload(); this.ezspCustomFrameHandler(payload); break; } case EzspFrameID.STACK_STATUS_HANDLER: { - const status = this.buffalo.readStatus(this.version); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspStackStatusHandler(status); break; } case EzspFrameID.ENERGY_SCAN_RESULT_HANDLER: { - const channel = this.buffalo.readUInt8(); - const maxRssiValue = this.buffalo.readInt8(); + const channel = this.callbackBuffalo.readUInt8(); + const maxRssiValue = this.callbackBuffalo.readInt8(); this.ezspEnergyScanResultHandler(channel, maxRssiValue); break; } case EzspFrameID.NETWORK_FOUND_HANDLER: { - const networkFound: EmberZigbeeNetwork = this.buffalo.readEmberZigbeeNetwork(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const networkFound: EmberZigbeeNetwork = this.callbackBuffalo.readEmberZigbeeNetwork(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); this.ezspNetworkFoundHandler(networkFound, lastHopLqi, lastHopRssi); break; } case EzspFrameID.SCAN_COMPLETE_HANDLER: { - const channel = this.buffalo.readUInt8(); - const status = this.buffalo.readStatus(this.version); + const channel = this.callbackBuffalo.readUInt8(); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspScanCompleteHandler(channel, status); break; } case EzspFrameID.UNUSED_PAN_ID_FOUND_HANDLER: { - const panId: PanId = this.buffalo.readUInt16(); - const channel = this.buffalo.readUInt8(); + const panId: PanId = this.callbackBuffalo.readUInt16(); + const channel = this.callbackBuffalo.readUInt8(); this.ezspUnusedPanIdFoundHandler(panId, channel); break; } case EzspFrameID.CHILD_JOIN_HANDLER: { - const index = this.buffalo.readUInt8(); - const joining = this.buffalo.readUInt8() !== 0; - const childId: NodeId = this.buffalo.readUInt16(); - const childEui64 = this.buffalo.readIeeeAddr(); - const childType: EmberNodeType = this.buffalo.readUInt8(); + const index = this.callbackBuffalo.readUInt8(); + const joining = this.callbackBuffalo.readUInt8() !== 0; + const childId: NodeId = this.callbackBuffalo.readUInt16(); + const childEui64 = this.callbackBuffalo.readIeeeAddr(); + const childType: EmberNodeType = this.callbackBuffalo.readUInt8(); this.ezspChildJoinHandler(index, joining, childId, childEui64, childType); break; } case EzspFrameID.DUTY_CYCLE_HANDLER: { - const channelPage = this.buffalo.readUInt8(); - const channel = this.buffalo.readUInt8(); - const state: EmberDutyCycleState = this.buffalo.readUInt8(); - const totalDevices = this.buffalo.readUInt8(); - const arrayOfDeviceDutyCycles: EmberPerDeviceDutyCycle[] = this.buffalo.readEmberPerDeviceDutyCycle(); + const channelPage = this.callbackBuffalo.readUInt8(); + const channel = this.callbackBuffalo.readUInt8(); + const state: EmberDutyCycleState = this.callbackBuffalo.readUInt8(); + const totalDevices = this.callbackBuffalo.readUInt8(); + const arrayOfDeviceDutyCycles: EmberPerDeviceDutyCycle[] = this.callbackBuffalo.readEmberPerDeviceDutyCycle(); this.ezspDutyCycleHandler(channelPage, channel, state, totalDevices, arrayOfDeviceDutyCycles); break; } case EzspFrameID.REMOTE_SET_BINDING_HANDLER: { - const entry : EmberBindingTableEntry = this.buffalo.readEmberBindingTableEntry(); - const index = this.buffalo.readUInt8(); - const policyDecision = this.buffalo.readStatus(this.version); + const entry : EmberBindingTableEntry = this.callbackBuffalo.readEmberBindingTableEntry(); + const index = this.callbackBuffalo.readUInt8(); + const policyDecision = this.callbackBuffalo.readStatus(this.version); this.ezspRemoteSetBindingHandler(entry, index, policyDecision); break; } case EzspFrameID.REMOTE_DELETE_BINDING_HANDLER: { - const index = this.buffalo.readUInt8(); - const policyDecision = this.buffalo.readStatus(this.version); + const index = this.callbackBuffalo.readUInt8(); + const policyDecision = this.callbackBuffalo.readStatus(this.version); this.ezspRemoteDeleteBindingHandler(index, policyDecision); break; } case EzspFrameID.MESSAGE_SENT_HANDLER: { if (this.version < 0x0E) { - const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); - const indexOrDestination = this.buffalo.readUInt16(); - const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); - const messageTag = this.buffalo.readUInt8(); - const status = this.buffalo.readUInt8(); + const type: EmberOutgoingMessageType = this.callbackBuffalo.readUInt8(); + const indexOrDestination = this.callbackBuffalo.readUInt16(); + const apsFrame: EmberApsFrame = this.callbackBuffalo.readEmberApsFrame(); + const messageTag = this.callbackBuffalo.readUInt8(); + const status = this.callbackBuffalo.readStatus(this.version); // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely - // const messageContents = this.buffalo.readPayload(); + // const messageContents = this.callbackBuffalo.readPayload(); this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag); } else { - const status = this.buffalo.readUInt32(); - const type: EmberOutgoingMessageType = this.buffalo.readUInt8(); - const indexOrDestination = this.buffalo.readUInt16(); - const apsFrame: EmberApsFrame = this.buffalo.readEmberApsFrame(); - const messageTag = this.buffalo.readUInt16(); + const status = this.callbackBuffalo.readUInt32(); + const type: EmberOutgoingMessageType = this.callbackBuffalo.readUInt8(); + const indexOrDestination = this.callbackBuffalo.readUInt16(); + const apsFrame: EmberApsFrame = this.callbackBuffalo.readEmberApsFrame(); + const messageTag = this.callbackBuffalo.readUInt16(); // EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY set to messageTag only, so skip parsing entirely - // const messageContents = this.buffalo.readPayload(); + // const messageContents = this.callbackBuffalo.readPayload(); this.ezspMessageSentHandler(status, type, indexOrDestination, apsFrame, messageTag); } break; } case EzspFrameID.POLL_COMPLETE_HANDLER: { - const status = this.buffalo.readStatus(this.version); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspPollCompleteHandler(status); break; } case EzspFrameID.POLL_HANDLER: { - const childId: NodeId = this.buffalo.readUInt16(); - const transmitExpected = this.buffalo.readUInt8() !== 0; + const childId: NodeId = this.callbackBuffalo.readUInt16(); + const transmitExpected = this.callbackBuffalo.readUInt8() !== 0; this.ezspPollHandler(childId, transmitExpected); break; } case EzspFrameID.INCOMING_MESSAGE_HANDLER: { if (this.version < 0x0E) { - const type: EmberIncomingMessageType = this.buffalo.readUInt8(); - const apsFrame = this.buffalo.readEmberApsFrame(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const senderShortId = this.buffalo.readUInt16(); - const bindingIndex = this.buffalo.readUInt8(); - const addressIndex = this.buffalo.readUInt8(); + const type: EmberIncomingMessageType = this.callbackBuffalo.readUInt8(); + const apsFrame = this.callbackBuffalo.readEmberApsFrame(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); + const senderShortId = this.callbackBuffalo.readUInt16(); + const bindingIndex = this.callbackBuffalo.readUInt8(); + const addressIndex = this.callbackBuffalo.readUInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId, senderLongId: ZSpec.BLANK_EUI64, @@ -812,56 +781,56 @@ export class Ezsp extends EventEmitter { lastHopRssi, lastHopTimestamp: 0 }; - const messageContents = this.buffalo.readPayload(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); } else { - const type: EmberIncomingMessageType = this.buffalo.readUInt8(); - const apsFrame = this.buffalo.readEmberApsFrame(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); - const messageContents = this.buffalo.readPayload(); + const type: EmberIncomingMessageType = this.callbackBuffalo.readUInt8(); + const apsFrame = this.callbackBuffalo.readEmberApsFrame(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspIncomingMessageHandler(type, apsFrame, packetInfo, messageContents); } break; } case EzspFrameID.INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER: { - const source: NodeId = this.buffalo.readUInt16(); - const longId = this.buffalo.readIeeeAddr(); - const cost = this.buffalo.readUInt8(); + const source: NodeId = this.callbackBuffalo.readUInt16(); + const longId = this.callbackBuffalo.readIeeeAddr(); + const cost = this.callbackBuffalo.readUInt8(); this.ezspIncomingManyToOneRouteRequestHandler(source, longId, cost); break; } case EzspFrameID.INCOMING_ROUTE_ERROR_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const target: NodeId = this.buffalo.readUInt16(); + const status = this.callbackBuffalo.readStatus(this.version); + const target: NodeId = this.callbackBuffalo.readUInt16(); this.ezspIncomingRouteErrorHandler(status, target); break; } case EzspFrameID.INCOMING_NETWORK_STATUS_HANDLER: { - const errorCode = this.buffalo.readUInt8(); - const target: NodeId = this.buffalo.readUInt16(); + const errorCode = this.callbackBuffalo.readUInt8(); + const target: NodeId = this.callbackBuffalo.readUInt16(); this.ezspIncomingNetworkStatusHandler(errorCode, target); break; } case EzspFrameID.INCOMING_ROUTE_RECORD_HANDLER: { - const source: NodeId = this.buffalo.readUInt16(); - const sourceEui = this.buffalo.readIeeeAddr(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); - const relayCount = this.buffalo.readUInt8(); - const relayList = this.buffalo.readListUInt16(relayCount);//this.buffalo.readListUInt8(relayCount * 2); + const source: NodeId = this.callbackBuffalo.readUInt16(); + const sourceEui = this.callbackBuffalo.readIeeeAddr(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); + const relayCount = this.callbackBuffalo.readUInt8(); + const relayList = this.callbackBuffalo.readListUInt16(relayCount);//this.callbackBuffalo.readListUInt8(relayCount * 2); this.ezspIncomingRouteRecordHandler(source, sourceEui, lastHopLqi, lastHopRssi, relayCount, relayList); break; } case EzspFrameID.ID_CONFLICT_HANDLER: { - const id: NodeId = this.buffalo.readUInt16(); + const id: NodeId = this.callbackBuffalo.readUInt16(); this.ezspIdConflictHandler(id); break; } case EzspFrameID.MAC_PASSTHROUGH_MESSAGE_HANDLER: { if (this.version < 0x0E) { - const messageType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const messageType: EmberMacPassthroughType = this.callbackBuffalo.readUInt8(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId: ZSpec.NULL_NODE_ID, senderLongId: ZSpec.BLANK_EUI64, @@ -871,22 +840,22 @@ export class Ezsp extends EventEmitter { lastHopRssi, lastHopTimestamp: 0 }; - const messageContents = this.buffalo.readPayload(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents); } else { - const messageType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); - const messageContents = this.buffalo.readPayload(); + const messageType: EmberMacPassthroughType = this.callbackBuffalo.readUInt8(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspMacPassthroughMessageHandler(messageType, packetInfo, messageContents); } break; } case EzspFrameID.MAC_FILTER_MATCH_MESSAGE_HANDLER: { if (this.version < 0x0E) { - const filterIndexMatch = this.buffalo.readUInt8(); - const legacyPassthroughType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const filterIndexMatch = this.callbackBuffalo.readUInt8(); + const legacyPassthroughType: EmberMacPassthroughType = this.callbackBuffalo.readUInt8(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId: ZSpec.NULL_NODE_ID, senderLongId: ZSpec.BLANK_EUI64, @@ -896,98 +865,98 @@ export class Ezsp extends EventEmitter { lastHopRssi, lastHopTimestamp: 0 }; - const messageContents = this.buffalo.readPayload(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents); } else { - const filterIndexMatch = this.buffalo.readUInt8(); - const legacyPassthroughType: EmberMacPassthroughType = this.buffalo.readUInt8(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); - const messageContents = this.buffalo.readPayload(); + const filterIndexMatch = this.callbackBuffalo.readUInt8(); + const legacyPassthroughType: EmberMacPassthroughType = this.callbackBuffalo.readUInt8(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, packetInfo, messageContents); } break; } case EzspFrameID.RAW_TRANSMIT_COMPLETE_HANDLER: { if (this.version < 0x0E) { - const status = this.buffalo.readUInt8(); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspRawTransmitCompleteHandler(Buffer.alloc(0), status); } else { - const messageContents = this.buffalo.readPayload(); - const status = this.buffalo.readUInt32(); + const messageContents = this.callbackBuffalo.readPayload(); + const status = this.callbackBuffalo.readUInt32(); this.ezspRawTransmitCompleteHandler(messageContents, status); } break; } case EzspFrameID.SWITCH_NETWORK_KEY_HANDLER: { - const sequenceNumber = this.buffalo.readUInt8(); + const sequenceNumber = this.callbackBuffalo.readUInt8(); this.ezspSwitchNetworkKeyHandler(sequenceNumber); break; } case EzspFrameID.ZIGBEE_KEY_ESTABLISHMENT_HANDLER: { - const partner = this.buffalo.readIeeeAddr(); - const status: EmberKeyStatus = this.buffalo.readUInt8(); + const partner = this.callbackBuffalo.readIeeeAddr(); + const status: EmberKeyStatus = this.callbackBuffalo.readUInt8(); this.ezspZigbeeKeyEstablishmentHandler(partner, status); break; } case EzspFrameID.TRUST_CENTER_JOIN_HANDLER: { - const newNodeId: NodeId = this.buffalo.readUInt16(); - const newNodeEui64 = this.buffalo.readIeeeAddr(); - const status: EmberDeviceUpdate = this.buffalo.readUInt8(); - const policyDecision: EmberJoinDecision = this.buffalo.readUInt8(); - const parentOfNewNodeId: NodeId = this.buffalo.readUInt16(); + const newNodeId: NodeId = this.callbackBuffalo.readUInt16(); + const newNodeEui64 = this.callbackBuffalo.readIeeeAddr(); + const status: EmberDeviceUpdate = this.callbackBuffalo.readUInt8(); + const policyDecision: EmberJoinDecision = this.callbackBuffalo.readUInt8(); + const parentOfNewNodeId: NodeId = this.callbackBuffalo.readUInt16(); this.ezspTrustCenterJoinHandler(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId); break; } case EzspFrameID.GENERATE_CBKE_KEYS_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const ephemeralPublicKey: EmberPublicKeyData = this.buffalo.readEmberPublicKeyData(); + const status = this.callbackBuffalo.readStatus(this.version); + const ephemeralPublicKey: EmberPublicKeyData = this.callbackBuffalo.readEmberPublicKeyData(); this.ezspGenerateCbkeKeysHandler(status, ephemeralPublicKey); break; } case EzspFrameID.CALCULATE_SMACS_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const initiatorSmac: EmberSmacData = this.buffalo.readEmberSmacData(); - const responderSmac: EmberSmacData = this.buffalo.readEmberSmacData(); + const status = this.callbackBuffalo.readStatus(this.version); + const initiatorSmac: EmberSmacData = this.callbackBuffalo.readEmberSmacData(); + const responderSmac: EmberSmacData = this.callbackBuffalo.readEmberSmacData(); this.ezspCalculateSmacsHandler(status, initiatorSmac, responderSmac); break; } case EzspFrameID.GENERATE_CBKE_KEYS_HANDLER283K1: { - const status = this.buffalo.readStatus(this.version); - const ephemeralPublicKey: EmberPublicKey283k1Data = this.buffalo.readEmberPublicKey283k1Data(); + const status = this.callbackBuffalo.readStatus(this.version); + const ephemeralPublicKey: EmberPublicKey283k1Data = this.callbackBuffalo.readEmberPublicKey283k1Data(); this.ezspGenerateCbkeKeysHandler283k1(status, ephemeralPublicKey); break; } case EzspFrameID.CALCULATE_SMACS_HANDLER283K1: { - const status = this.buffalo.readStatus(this.version); - const initiatorSmac: EmberSmacData = this.buffalo.readEmberSmacData(); - const responderSmac: EmberSmacData = this.buffalo.readEmberSmacData(); + const status = this.callbackBuffalo.readStatus(this.version); + const initiatorSmac: EmberSmacData = this.callbackBuffalo.readEmberSmacData(); + const responderSmac: EmberSmacData = this.callbackBuffalo.readEmberSmacData(); this.ezspCalculateSmacsHandler283k1(status, initiatorSmac, responderSmac); break; } case EzspFrameID.DSA_SIGN_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const messageContents = this.buffalo.readPayload(); + const status = this.callbackBuffalo.readStatus(this.version); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspDsaSignHandler(status, messageContents); break; } case EzspFrameID.DSA_VERIFY_HANDLER: { - const status = this.buffalo.readStatus(this.version); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspDsaVerifyHandler(status); break; } case EzspFrameID.MFGLIB_RX_HANDLER: { - const linkQuality = this.buffalo.readUInt8(); - const rssi = this.buffalo.readInt8(); - const packetLength = this.buffalo.readUInt8(); - const packetContents = this.buffalo.readListUInt8(packetLength); + const linkQuality = this.callbackBuffalo.readUInt8(); + const rssi = this.callbackBuffalo.readInt8(); + const packetLength = this.callbackBuffalo.readUInt8(); + const packetContents = this.callbackBuffalo.readListUInt8(packetLength); this.ezspMfglibRxHandler(linkQuality, rssi, packetLength, packetContents); break; } case EzspFrameID.INCOMING_BOOTLOAD_MESSAGE_HANDLER: { if (this.version < 0x0E) { - const longId = this.buffalo.readIeeeAddr(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const longId = this.callbackBuffalo.readIeeeAddr(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId: ZSpec.NULL_NODE_ID, senderLongId: ZSpec.BLANK_EUI64, @@ -997,35 +966,35 @@ export class Ezsp extends EventEmitter { lastHopRssi, lastHopTimestamp: 0, }; - const messageContents = this.buffalo.readPayload(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents); } else { - const longId = this.buffalo.readIeeeAddr(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); - const messageContents = this.buffalo.readPayload(); + const longId = this.callbackBuffalo.readIeeeAddr(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspIncomingBootloadMessageHandler(longId, packetInfo, messageContents); } break; } case EzspFrameID.BOOTLOAD_TRANSMIT_COMPLETE_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const messageContents = this.buffalo.readPayload(); + const status = this.callbackBuffalo.readStatus(this.version); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspBootloadTransmitCompleteHandler(status, messageContents); break; } case EzspFrameID.INCOMING_MFG_TEST_MESSAGE_HANDLER: { - const messageType = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); + const messageType = this.callbackBuffalo.readUInt8(); + const messageContents = this.callbackBuffalo.readPayload(); this.ezspIncomingMfgTestMessageHandler(messageType, messageContents); break; } case EzspFrameID.ZLL_NETWORK_FOUND_HANDLER: { if (this.version < 0x0E) { - const networkInfo = this.buffalo.readEmberZllNetwork(); - const isDeviceInfoNull = this.buffalo.readUInt8() !== 0; - const deviceInfo = this.buffalo.readEmberZllDeviceInfoRecord(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const networkInfo = this.callbackBuffalo.readEmberZllNetwork(); + const isDeviceInfoNull = this.callbackBuffalo.readUInt8() !== 0; + const deviceInfo = this.callbackBuffalo.readEmberZllDeviceInfoRecord(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId: ZSpec.NULL_NODE_ID, senderLongId: ZSpec.BLANK_EUI64, @@ -1037,24 +1006,24 @@ export class Ezsp extends EventEmitter { }; this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo); } else { - const networkInfo = this.buffalo.readEmberZllNetwork(); - const isDeviceInfoNull = this.buffalo.readUInt8() !== 0; - const deviceInfo = this.buffalo.readEmberZllDeviceInfoRecord(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const networkInfo = this.callbackBuffalo.readEmberZllNetwork(); + const isDeviceInfoNull = this.callbackBuffalo.readUInt8() !== 0; + const deviceInfo = this.callbackBuffalo.readEmberZllDeviceInfoRecord(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, packetInfo); } break; } case EzspFrameID.ZLL_SCAN_COMPLETE_HANDLER: { - const status = this.buffalo.readStatus(this.version); + const status = this.callbackBuffalo.readStatus(this.version); this.ezspZllScanCompleteHandler(status); break; } case EzspFrameID.ZLL_ADDRESS_ASSIGNMENT_HANDLER: { if (this.version < 0x0E) { - const addressInfo = this.buffalo.readEmberZllAddressAssignment(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readInt8(); + const addressInfo = this.callbackBuffalo.readEmberZllAddressAssignment(); + const lastHopLqi = this.callbackBuffalo.readUInt8(); + const lastHopRssi = this.callbackBuffalo.readInt8(); const packetInfo: EmberRxPacketInfo = { senderShortId: ZSpec.NULL_NODE_ID, senderLongId: ZSpec.BLANK_EUI64, @@ -1066,37 +1035,37 @@ export class Ezsp extends EventEmitter { }; this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo); } else { - const addressInfo = this.buffalo.readEmberZllAddressAssignment(); - const packetInfo = this.buffalo.readEmberRxPacketInfo(); + const addressInfo = this.callbackBuffalo.readEmberZllAddressAssignment(); + const packetInfo = this.callbackBuffalo.readEmberRxPacketInfo(); this.ezspZllAddressAssignmentHandler(addressInfo, packetInfo); } break; } case EzspFrameID.ZLL_TOUCH_LINK_TARGET_HANDLER: { - const networkInfo = this.buffalo.readEmberZllNetwork(); + const networkInfo = this.callbackBuffalo.readEmberZllNetwork(); this.ezspZllTouchLinkTargetHandler(networkInfo); break; } case EzspFrameID.D_GP_SENT_HANDLER: { - const status = this.buffalo.readStatus(this.version); - const gpepHandle = this.buffalo.readUInt8(); + const status = this.callbackBuffalo.readStatus(this.version); + const gpepHandle = this.callbackBuffalo.readUInt8(); this.ezspDGpSentHandler(status, gpepHandle); break; } case EzspFrameID.GPEP_INCOMING_MESSAGE_HANDLER: { - const gpStatus = this.buffalo.readUInt8(); - const gpdLink = this.buffalo.readUInt8(); - const sequenceNumber = this.buffalo.readUInt8(); - const addr = this.buffalo.readEmberGpAddress(); - const gpdfSecurityLevel: EmberGpSecurityLevel = this.buffalo.readUInt8(); - const gpdfSecurityKeyType: EmberGpKeyType = this.buffalo.readUInt8(); - const autoCommissioning = this.buffalo.readUInt8() !== 0; - const bidirectionalInfo = this.buffalo.readUInt8(); - const gpdSecurityFrameCounter = this.buffalo.readUInt32(); - const gpdCommandId = this.buffalo.readUInt8(); - const mic = this.buffalo.readUInt32(); - const proxyTableIndex = this.buffalo.readUInt8(); - const gpdCommandPayload = this.buffalo.readPayload(); + const gpStatus = this.callbackBuffalo.readUInt8(); + const gpdLink = this.callbackBuffalo.readUInt8(); + const sequenceNumber = this.callbackBuffalo.readUInt8(); + const addr = this.callbackBuffalo.readEmberGpAddress(); + const gpdfSecurityLevel: EmberGpSecurityLevel = this.callbackBuffalo.readUInt8(); + const gpdfSecurityKeyType: EmberGpKeyType = this.callbackBuffalo.readUInt8(); + const autoCommissioning = this.callbackBuffalo.readUInt8() !== 0; + const bidirectionalInfo = this.callbackBuffalo.readUInt8(); + const gpdSecurityFrameCounter = this.callbackBuffalo.readUInt32(); + const gpdCommandId = this.callbackBuffalo.readUInt8(); + const mic = this.callbackBuffalo.readUInt32(); + const proxyTableIndex = this.callbackBuffalo.readUInt8(); + const gpdCommandPayload = this.callbackBuffalo.readPayload(); this.ezspGpepIncomingMessageHandler( gpStatus, gpdLink, @@ -1115,7 +1084,7 @@ export class Ezsp extends EventEmitter { break; } default: - this.ezspErrorHandler(EzspStatus.ERROR_INVALID_FRAME_ID); + logger.debug(`<=x= Ignored unused/unknown ${this.callbackFrameToString}`, NS); } } @@ -2256,13 +2225,13 @@ export class Ezsp extends EventEmitter { async ezspGetLibraryStatus(libraryId: EmberLibraryId): Promise { this.startCommand(EzspFrameID.GET_LIBRARY_STATUS); this.buffalo.writeUInt8(libraryId); - + const sendStatus = await this.sendCommand(); if (sendStatus !== EzspStatus.SUCCESS) { throw new EzspError(sendStatus); } - + const status = this.buffalo.readUInt8(); return status; @@ -2608,6 +2577,7 @@ export class Ezsp extends EventEmitter { * dynamic value. Therefore, you should call this function whenever the value * changes. * @param descriptor uint16_t The new power descriptor for the local node. + * @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-. */ async ezspSetPowerDescriptor(descriptor: number): Promise { this.startCommand(EzspFrameID.SET_POWER_DESCRIPTOR); @@ -4234,7 +4204,7 @@ export class Ezsp extends EventEmitter { /** * Clears all cached beacons that have been collected from a scan. - * @returns An SLStatus value indicating success or the reason for failure. + * @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-. */ async ezspClearStoredBeacons(): Promise { this.startCommand(EzspFrameID.CLEAR_STORED_BEACONS); @@ -4741,14 +4711,16 @@ export class Ezsp extends EventEmitter { /** * Sends a broadcast message as per the ZigBee specification. - * @param alias The aliased source from which we send the broadcast. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source + * @param alias uint16_t (unused in v13-) The aliased source from which we send the broadcast. + * This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source * @param destination The destination to which to send the broadcast. This must be one of the three ZigBee broadcast addresses. - * @param nwkSequence The alias nwk sequence number. This won't be used if there is no aliased source. + * @param nwkSequence uint8_t (unused in v13-) The alias nwk sequence number. This won't be used if there is no aliased source. * @param apsFrame EmberApsFrame * The APS frame for the message. - * @param radius uint8_t (v14+: uint16_t) The message will be delivered to all nodes within radius hops of the sender. + * @param radius uint8_t The message will be delivered to all nodes within radius hops of the sender. * A radius of zero is converted to EMBER_MAX_HOPS. - * @param uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. - * @param uint8_t * The broadcast message. + * @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host. + * This value is used in the ezspMessageSentHandler response to refer to this message. + * @param messageContents uint8_t * The broadcast message. * @returns An SLStatus value indicating success or the reason for failure. * @returns uint8_t * The sequence number that will be used when this message is transmitted. */ @@ -4814,12 +4786,13 @@ export class Ezsp extends EventEmitter { * @param apsFrame EmberApsFrame * The APS frame for the message. The multicast will be sent to the groupId in this frame. * @param hops uint8_t The message will be delivered to all nodes within this number of hops of the sender. * A value of zero is converted to EMBER_MAX_HOPS. - * @param broadcastAddr The number of hops that the message will be forwarded by devices that are not members of the group. + * @param broadcastAddr uint16_t (unused in v13-) The number of hops that the message will be forwarded by devices + * that are not members of the group. * A value of 7 or greater is treated as infinite. - * @param alias The alias source address. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source - * @param nwkSequence The alias sequence number. This won't be used if there is no aliased source. - * @param messageTag uint8_t A value chosen by the Host. This value is used in the ezspMessageSentHandler response to refer to this message. - * @param messageLength uint8_t (v14+: uint16_t) The length of the messageContents parameter in bytes. + * @param alias uint16_t (unused in v13-) The alias source address. This must be SL_ZIGBEE_NULL_NODE_ID if we do not need an aliased source + * @param nwkSequence uint8_t (unused in v13-) The alias sequence number. This won't be used if there is no aliased source. + * @param messageTag uint8_t (v14+: uint16_t) A value chosen by the Host. + * This value is used in the ezspMessageSentHandler response to refer to this message. * @param messageContents uint8_t * The multicast message. * @returns An SLStatus value. For any result other than SLStatus.OK, the message will not be sent. * - SLStatus.OK - The message has been submitted for transmission. @@ -4868,7 +4841,7 @@ export class Ezsp extends EventEmitter { * @param apsFrame EmberApsFrame * Value supplied by incoming unicast. * @param uint8_t The length of the messageContents parameter in bytes. * @param uint8_t * The reply message. - * @returns An EmberStatus value. + * @returns * - SLStatus.INVALID_STATE - The SL_ZIGBEE_EZSP_UNICAST_REPLIES_POLICY is set to SL_ZIGBEE_EZSP_HOST_WILL_NOT_SUPPLY_REPLY. * This means the NCP will automatically send an empty reply. The Host must change * the policy to SL_ZIGBEE_EZSP_HOST_WILL_SUPPLY_REPLY before it can supply the reply. @@ -5423,6 +5396,7 @@ export class Ezsp extends EventEmitter { * @param remoteEui64 The address of the node for which the timeout is to be set. * @param extendedTimeout true if the retry interval should be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. * false if the normal retry interval should be used. + * @returns An SLStatus value indicating success or the reason for failure. Always `OK` in v13-. */ async ezspSetExtendedTimeout(remoteEui64: EUI64, extendedTimeout: boolean): Promise { this.startCommand(EzspFrameID.SET_EXTENDED_TIMEOUT); @@ -6586,7 +6560,7 @@ export class Ezsp extends EventEmitter { * Import a transient link key. * @param eui64 EUI64 associated with this transient key. * @param plaintextKey sl_zb_sec_man_key_t * The key to import. - * @param flags sl_zigbee_sec_man_flags_t (v13-) Flags associated with this transient key. + * @param flags sl_zigbee_sec_man_flags_t (unused in v14+) Flags associated with this transient key. * @returns Status of key import operation. */ async ezspImportTransientKey(eui64: EUI64, plaintextKey: SecManKey, flags: SecManFlag = SecManFlag.NONE): Promise { diff --git a/src/adapter/ember/uart/ash.ts b/src/adapter/ember/uart/ash.ts index 797e9fb61f..0058d671a5 100644 --- a/src/adapter/ember/uart/ash.ts +++ b/src/adapter/ember/uart/ash.ts @@ -714,7 +714,7 @@ export class UartAsh extends EventEmitter { return EzspStatus.ERROR_INVALID_CALL; } - logger.info(`======== ASH NCP reset ========`, NS); + logger.info(`======== ASH Adapter reset ========`, NS); this.initVariables(); @@ -1101,7 +1101,7 @@ export class UartAsh extends EventEmitter { return EzspStatus.SUCCESS; } else if (frameType === AshFrameType.ERROR) { - logger.error(`Received ERROR from NCP while connecting, with code=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); + logger.error(`Received ERROR from adapter while connecting, with code=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); return this.ncpDisconnect(EzspStatus.ASH_NCP_FATAL_ERROR); } @@ -1179,9 +1179,7 @@ export class UartAsh extends EventEmitter { this.counters.rxData += this.rxDataBuffer.len; - setImmediate(() => { - this.emit(AshEvents.FRAME); - }); + setImmediate(this.emit.bind(this, AshEvents.FRAME)); return EzspStatus.SUCCESS; } else { // frame is out of sequence @@ -1211,13 +1209,13 @@ export class UartAsh extends EventEmitter { break; case AshFrameType.RSTACK: // unexpected ncp reset - logger.error(`Received unexpected reset from NCP, with reason=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); + logger.error(`Received unexpected reset from adapter, with reason=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); this.ncpError = EzspStatus.ASH_NCP_FATAL_ERROR; return this.hostDisconnect(EzspStatus.ASH_ERROR_NCP_RESET); case AshFrameType.ERROR: // ncp error - logger.error(`Received ERROR from NCP, with code=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); + logger.error(`Received ERROR from adapter, with code=${NcpFailedCode[this.rxSHBuffer[2]]}.`, NS); return this.ncpDisconnect(EzspStatus.ASH_NCP_FATAL_ERROR); case AshFrameType.INVALID: // reject invalid frames @@ -1367,7 +1365,7 @@ export class UartAsh extends EventEmitter { if (this.rxFree.length < CONFIG_NR_LOW_LIMIT) { this.flags |= Flag.NR; - logger.warning(`NOT READY - Signaling NCP`, NS); + logger.warning(`NOT READY - Signaling adapter`, NS); } else if (this.rxFree.length > CONFIG_NR_HIGH_LIMIT) { this.flags &= ~Flag.NR; @@ -1407,8 +1405,8 @@ export class UartAsh extends EventEmitter { this.flags = 0; this.hostError = error; - logger.error(`ASH disconnected: ${EzspStatus[error]} | NCP status: ${EzspStatus[this.ncpError]}`, NS); - + logger.error(`ASH disconnected: ${EzspStatus[error]} | Adapter status: ${EzspStatus[this.ncpError]}`, NS); + return EzspStatus.HOST_FATAL_ERROR; } @@ -1421,7 +1419,7 @@ export class UartAsh extends EventEmitter { this.flags = 0; this.ncpError = error; - logger.error(`ASH disconnected | NCP status: ${EzspStatus[this.ncpError]}`, NS); + logger.error(`ASH disconnected | Adapter status: ${EzspStatus[this.ncpError]}`, NS); return EzspStatus.ASH_NCP_FATAL_ERROR; } diff --git a/test/adapter/ember/consts.ts b/test/adapter/ember/consts.ts index 945ee4d32d..29ac1b77be 100644 --- a/test/adapter/ember/consts.ts +++ b/test/adapter/ember/consts.ts @@ -93,3 +93,41 @@ export const SEND_DATA_VERSION = Buffer.from('004221a8597c057e', 'hex'); /** protocolVersion: 13, stackType: 2, stackVersion: 29712 */ export const RCED_DATA_VERSION = Buffer.from('0142a1a8592805c6a8777e', 'hex'); export const RCED_DATA_VERSION_RES = [13, 2, 29712]; + + +// [ZCL to=51678 apsFrame={"profileId":260,"clusterId":6,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":0} header={"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":false,"manufacturerSpecific":false},"manufacturerCode":null,"transactionSequenceNumber":4,"commandIdentifier":0}] +// [FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=25] +// [FRAME type=DATA frmTx=7 frmRx=0] +export const SEND_UNICAST_ASH_RAW = '706521a9602a156c90904b23aa5493098d4e27abeece648af9c677b97e'; +// [FRAME type=DATA ackNum=0 frmNum=0] +export const SEND_UNICAST_REPLY_FN0_ASH_RAW = '0065a1a9602a15b25994d0954c7e'; +// [FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=10] +// [SENT type=DIRECT apsSequence=154 messageTag=3 status=OK] +export const SEND_UNICAST_REPLY_FN0_EZSP = '2780013400000000009a'; + +// [FRAME type=DATA ackNum=0 frmNum=1] +export const MESSAGE_SENT_HANDLER_FN1_ASH_RAW = '1065b1a96b2a15b259944afb6351934f9c4f26ebfcce677d31fec66357897e'; +// [CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26] +// ezspMessageSentHandler(): callback called with: [status=OK], [type=DIRECT], [indexOrDestination=51678], [apsFrame={"profileId":260,"clusterId":6,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":154}], [messageTag=3] +export const MESSAGE_SENT_HANDLER_FN1_EZSP = '2790013f000000000000dec9040106000101401100009a030000'; + +// [FRAME type=DATA ackNum=0 frmNum=2] +export const INCOMING_MESSAGE_HANDLER_FN2_ASH_RAW = '2065b1a97d312a15b658924a24ab5593499c3ef962edce678bfdc6638903813ca7edcdde6f8ae7c3d0d5d26b68937e'; +// [CBFRAME: ID=69:"INCOMING_MESSAGE_HANDLER" Seq=39 Len=42] +// ezspIncomingMessageHandler(): callback called with: [type=UNICAST], [apsFrame={"profileId":260,"clusterId":6,"sourceEndpoint":1,"destinationEndpoint":1,"options":256,"groupId":0,"sequence":112}], [packetInfo:{"senderShortId":51678,"senderLongId":"0x0000000000000000","bindingIndex":255,"addressIndex":255,"lastHopLqi":3,"lastHopRssi":0,"lastHopTimestamp":6}], [messageContents=18040b0000] +// Received payload: clusterID=6, address=51678, groupID=0, endpoint=1, destinationEndpoint=1, wasBroadcast=false, linkQuality=3, frame={"header":{"frameControl":{"frameType":0,"manufacturerSpecific":false,"direction":1,"disableDefaultResponse":true,"reservedBits":0},"manufacturerCode":null,"transactionSequenceNumber":4,"commandIdentifier":11},"payload":{"cmdId":0,"statusCode":0},"command":{"ID":11,"name":"defaultRsp","parameters":[{"name":"cmdId","type":32},{"name":"statusCode","type":32}]}} +export const INCOMING_MESSAGE_HANDLER_FN2_EZSP = '2790014500000401060001010001000070dec90000000000000000ffff0300060000000518040b000002'; + +// [FRAME type=DATA ackNum=0 frmNum=0] +export const MESSAGE_SENT_HANDLER_FN0_ASH_RAW = '0065b1a96b2a15b259944afb6351934f9c4f26ebfcce6766fec66302907e'; +// [CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26] +// ezspMessageSentHandler(): callback called with: [status=OK], [type=DIRECT], [indexOrDestination=51678], [apsFrame={"profileId":260,"clusterId":6,"sourceEndpoint":1,"destinationEndpoint":1,"options":4416,"groupId":0,"sequence":237}], [messageTag=3] +export const MESSAGE_SENT_HANDLER_FN0_EZSP = '2790013f000000000000dec904010600010140110000ed030000'; + +// [FRAME: ID=85:"SET_POLICY" Seq=79 Len=7] +// [FRAME type=DATA frmTx=7 frmRx=0] +export const SET_POLICY_ASH_RAW = '700d21a9012a15b0f5667e'; +// [FRAME type=DATA ackNum=0 frmNum=1] +export const SET_POLICY_REPLY_FN1_ASH_RAW = '100da1a9012a15b259944fb87e'; +// [FRAME: ID=85:"SET_POLICY" Seq=79 Len=9] +export const SET_POLICY_REPLY_FN1_EZSP = '4f8001550000000000'; diff --git a/test/adapter/ember/ezsp.test.ts b/test/adapter/ember/ezsp.test.ts index e4054b5ce9..60ad6bcd6c 100644 --- a/test/adapter/ember/ezsp.test.ts +++ b/test/adapter/ember/ezsp.test.ts @@ -13,14 +13,23 @@ import { SEND_DATA_VERSION, SEND_ACK_FIRST_BYTES, RCED_ERROR_WATCHDOG_BYTES, + SEND_UNICAST_REPLY_FN0_ASH_RAW, + MESSAGE_SENT_HANDLER_FN1_ASH_RAW, + INCOMING_MESSAGE_HANDLER_FN2_ASH_RAW, + MESSAGE_SENT_HANDLER_FN0_ASH_RAW, + SET_POLICY_REPLY_FN1_ASH_RAW, } from './consts'; import {EzspStatus} from '../../../src/adapter/ember/enums'; import {AshEvents} from '../../../src/adapter/ember/uart/ash'; import {logger} from '../../../src/utils/logger'; -const emitFromSerial = (ezsp: Ezsp, data: Buffer): void => { +const emitFromSerial = async (ezsp: Ezsp, data: Buffer, skipAdvanceTimers: boolean = false): Promise => { //@ts-expect-error private ezsp.ash.serialPort.port.emitData(Buffer.from(data)); + + if (!skipAdvanceTimers) { + await jest.advanceTimersByTimeAsync(1000); + } }; const advanceTime100ms = async (times: number): Promise => { @@ -53,7 +62,7 @@ describe('Ember Ezsp Layer', () => { mock.mockClear(); } - ezsp = new Ezsp(5, openOpts); + ezsp = new Ezsp(openOpts); MockBinding.createPort('/dev/ttyACM0', {record: true, ...adapterSONOFFDongleE}); jest.useFakeTimers(); }); @@ -68,8 +77,7 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(POST_RSTACK_SERIAL_BYTES); @@ -83,10 +91,9 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTime100ms(2); - emitFromSerial(ezsp, RCED_DATA_WITH_CRC_ERROR); + await emitFromSerial(ezsp, RCED_DATA_WITH_CRC_ERROR, true); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(POST_RSTACK_SERIAL_BYTES); @@ -103,10 +110,9 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTime100ms(2); - emitFromSerial(ezsp, Buffer.from(RECD_ERROR_ACK_TIMEOUT_BYTES)); + await emitFromSerial(ezsp, Buffer.from(RECD_ERROR_ACK_TIMEOUT_BYTES), true); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(Buffer.concat([Buffer.from(SEND_RST_BYTES), POST_RSTACK_SERIAL_BYTES])); @@ -122,10 +128,9 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTime100ms(2); - emitFromSerial(ezsp, Buffer.from(RCED_ERROR_WATCHDOG_BYTES)); + await emitFromSerial(ezsp, Buffer.from(RCED_ERROR_WATCHDOG_BYTES), true); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(Buffer.concat([Buffer.from(SEND_RST_BYTES), POST_RSTACK_SERIAL_BYTES])); @@ -140,8 +145,6 @@ describe('Ember Ezsp Layer', () => { // @ts-expect-error private const onAshFatalErrorSpy = jest.spyOn(ezsp, 'onAshFatalError').mockImplementationOnce((status: EzspStatus): void => { // mimic EmberAdapter onNcpNeedsResetAndInit - logger.error(`!!! NCP FATAL ERROR reason=${EzspStatus[status]}. ATTEMPTING RESET... !!!`, 'jest:ember:ezsp'); - restart = new Promise(async (resolve) => { jest.useRealTimers(); await ezsp.stop(); @@ -150,20 +153,17 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); resolve(startResult); }); }); const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(POST_RSTACK_SERIAL_BYTES); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS);// dup is received after this returns expect(ezsp.checkConnection()).toBeFalsy(); // @ts-expect-error set via emit @@ -184,8 +184,6 @@ describe('Ember Ezsp Layer', () => { // @ts-expect-error private const onAshFatalErrorSpy = jest.spyOn(ezsp, 'onAshFatalError').mockImplementationOnce((status: EzspStatus): void => { // mimic EmberAdapter onNcpNeedsResetAndInit - logger.error(`!!! NCP FATAL ERROR reason=${EzspStatus[status]}. ATTEMPTING RESET... !!!`, 'jest:ember:ezsp'); - restart = new Promise(async (resolve) => { jest.useRealTimers(); await ezsp.stop(); @@ -194,22 +192,19 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); resolve(startResult); }); }); const startResult = ezsp.start(); await advanceTime100ms(2); - emitFromSerial(ezsp, Buffer.from(RCED_ERROR_WATCHDOG_BYTES)); + await emitFromSerial(ezsp, Buffer.from(RCED_ERROR_WATCHDOG_BYTES), true); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(Buffer.concat([Buffer.from(SEND_RST_BYTES), POST_RSTACK_SERIAL_BYTES])); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS);// dup is received after this returns expect(ezsp.checkConnection()).toBeFalsy(); // @ts-expect-error set via emit @@ -228,8 +223,6 @@ describe('Ember Ezsp Layer', () => { // @ts-expect-error private const onAshFatalErrorSpy = jest.spyOn(ezsp, 'onAshFatalError').mockImplementationOnce((status: EzspStatus): void => { // mimic EmberAdapter onNcpNeedsResetAndInit - logger.error(`!!! NCP FATAL ERROR reason=${EzspStatus[status]}. ATTEMPTING RESET... !!!`, 'jest:ember:ezsp'); - restart = new Promise(async (resolve) => { jest.useRealTimers(); await ezsp.stop(); @@ -238,16 +231,14 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); resolve(startResult); }); }); const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(POST_RSTACK_SERIAL_BYTES); @@ -257,7 +248,7 @@ describe('Ember Ezsp Layer', () => { const version = ezsp.ezspVersion(13); await jest.advanceTimersByTimeAsync(1000); - emitFromSerial(ezsp, RCED_DATA_VERSION); + await emitFromSerial(ezsp, RCED_DATA_VERSION); await jest.advanceTimersByTimeAsync(1000); await expect(version).resolves.toStrictEqual(RCED_DATA_VERSION_RES); //@ts-expect-error private @@ -267,8 +258,7 @@ describe('Ember Ezsp Layer', () => { await jest.advanceTimersByTimeAsync(10000);// any time after startup sequence - emitFromSerial(ezsp, Buffer.from(RECD_ERROR_ACK_TIMEOUT_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_ERROR_ACK_TIMEOUT_BYTES)); expect(ezsp.checkConnection()).toBeFalsy(); // @ts-expect-error set via emit expect(restart).toBeDefined(); @@ -284,8 +274,7 @@ describe('Ember Ezsp Layer', () => { const startResult = ezsp.start(); await advanceTimeToRSTACK(); - emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); - await jest.advanceTimersByTimeAsync(1000); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); await expect(startResult).resolves.toStrictEqual(EzspStatus.SUCCESS); //@ts-expect-error private expect(ezsp.ash.serialPort.port.recording).toStrictEqual(POST_RSTACK_SERIAL_BYTES); @@ -299,4 +288,101 @@ describe('Ember Ezsp Layer', () => { await ezsp.ezspVersion(13); }).rejects.toStrictEqual(EzspStatus[EzspStatus.NOT_CONNECTED]); }); + + describe('When connected', () => { + let callbackDispatchSpy: jest.SpyInstance; + let mockResponseWaiterResolve = jest.fn(); + + beforeEach(async () => { + const startResult = ezsp.start(); + + await advanceTimeToRSTACK(); + await emitFromSerial(ezsp, Buffer.from(RECD_RSTACK_BYTES)); + await startResult; + expect(ezsp.checkConnection()).toBeTruthy(); + + callbackDispatchSpy = jest.spyOn(ezsp, 'callbackDispatch').mockImplementation(jest.fn()); + + callbackDispatchSpy.mockClear(); + mockResponseWaiterResolve.mockClear(); + }); + + it('Parses successive valid incoming frames', async () => { + // @ts-expect-error private + ezsp.responseWaiter = {timer: setTimeout(() => {}, 15000), resolve: mockResponseWaiterResolve}; + + await emitFromSerial(ezsp, Buffer.from(SEND_UNICAST_REPLY_FN0_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(0); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledWith(EzspStatus.SUCCESS); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=10]`); + + await emitFromSerial(ezsp, Buffer.from(MESSAGE_SENT_HANDLER_FN1_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(1); + expect(ezsp.callbackFrameToString).toStrictEqual(`[CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26]`); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=10]`); + + await emitFromSerial(ezsp, Buffer.from(INCOMING_MESSAGE_HANDLER_FN2_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(2); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(1); + expect(ezsp.callbackFrameToString).toStrictEqual(`[CBFRAME: ID=69:"INCOMING_MESSAGE_HANDLER" Seq=39 Len=42]`); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=10]`); + }); + + it('Parses valid incoming callback frame while waiting for response frame', async () => { + // @ts-expect-error private + ezsp.responseWaiter = {timer: setTimeout(() => {}, 15000), resolve: mockResponseWaiterResolve}; + + await emitFromSerial(ezsp, Buffer.from(MESSAGE_SENT_HANDLER_FN0_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(0); + expect(ezsp.callbackFrameToString).toStrictEqual(`[CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26]`); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=0:"VERSION" Seq=0 Len=0]`); + + await emitFromSerial(ezsp, Buffer.from(SET_POLICY_REPLY_FN1_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledWith(EzspStatus.SUCCESS); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=85:"SET_POLICY" Seq=79 Len=9]`); + expect(ezsp.callbackFrameToString).toStrictEqual(`[CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26]`); + }); + + it('Parses invalid incoming frame', async () => { + jest.spyOn(ezsp, 'validateReceivedFrame').mockReturnValueOnce(EzspStatus.ERROR_WRONG_DIRECTION); + + // @ts-expect-error private + ezsp.responseWaiter = {timer: setTimeout(() => {}, 15000), resolve: mockResponseWaiterResolve}; + + await emitFromSerial(ezsp, Buffer.from(SEND_UNICAST_REPLY_FN0_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(0); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(1); + expect(mockResponseWaiterResolve).toHaveBeenCalledWith(EzspStatus.ERROR_WRONG_DIRECTION); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=52:"SEND_UNICAST" Seq=39 Len=10]`); + }); + + it('Parses invalid incoming callback frame', async () => { + jest.spyOn(ezsp, 'validateReceivedFrame').mockReturnValueOnce(EzspStatus.ERROR_WRONG_DIRECTION); + + await emitFromSerial(ezsp, Buffer.from(MESSAGE_SENT_HANDLER_FN0_ASH_RAW, 'hex')); + await jest.advanceTimersByTimeAsync(1000); + + expect(callbackDispatchSpy).toHaveBeenCalledTimes(0); + expect(mockResponseWaiterResolve).toHaveBeenCalledTimes(0); + expect(ezsp.callbackFrameToString).toStrictEqual(`[CBFRAME: ID=63:"MESSAGE_SENT_HANDLER" Seq=39 Len=26]`); + expect(ezsp.frameToString).toStrictEqual(`[FRAME: ID=0:"VERSION" Seq=0 Len=0]`); + }); + }); }); diff --git a/test/adapter/ember/requestQueue.test.ts b/test/adapter/ember/requestQueue.test.ts index 7e2ceb7759..15fb097be4 100644 --- a/test/adapter/ember/requestQueue.test.ts +++ b/test/adapter/ember/requestQueue.test.ts @@ -1,59 +1,50 @@ -import {EmberRequestQueue, BUSY_DEFER_MSEC, NETWORK_DOWN_DEFER_MSEC} from '../../../src/adapter/ember/adapter/requestQueue'; +import { + EmberRequestQueue, + BUSY_DEFER_MSEC, + NETWORK_DOWN_DEFER_MSEC, + MAX_SEND_ATTEMPTS, + HIGH_COUNT, +} from '../../../src/adapter/ember/adapter/requestQueue'; import {SLStatus, EzspStatus} from '../../../src/adapter/ember/enums'; import {EzspError} from '../../../src/adapter/ember/ezspError'; import {Wait} from '../../../src/utils'; -let fakeWaitTime = 1000; -let varyingReturn: SLStatus = SLStatus.OK; -const getVaryingReturn = async (): Promise => { - await Wait(fakeWaitTime); - return varyingReturn; -}; -const getThrownError = async (): Promise => { - await Wait(fakeWaitTime); - throw new EzspError(EzspStatus.ASH_ACK_TIMEOUT); -} -const getThrowNetworkBusy = async (): Promise => { - await Wait(fakeWaitTime); - throw new EzspError(EzspStatus.NO_TX_SPACE); -}; -const getThrowNetworkDown = async (): Promise => { - await Wait(fakeWaitTime); - throw new EzspError(EzspStatus.NOT_CONNECTED); -}; - -class TestThis { - public bs: boolean; - public q: EmberRequestQueue; - - constructor() { - this.bs = false; - this.q = new EmberRequestQueue(60); +describe('Ember Request Queue', () => { + let requestQueue: EmberRequestQueue; + let fakeWaitTime: number = 1000; + let varyingReturn: SLStatus = SLStatus.OK; + let deferSpy: jest.SpyInstance; + + const getVaryingReturn = async (): Promise => { + await Wait(fakeWaitTime); + return varyingReturn; + }; + + const getQueueEntryAt = (index: number, fromPriorityQueue: boolean = false) => { + // @ts-expect-error private + const queue = fromPriorityQueue ? requestQueue.priorityQueue : requestQueue.queue; + + return queue[index]; } - public async getNewBS(): Promise { - await new Promise((resolve, reject): void => { - this.q.enqueue( + const mockAdapterCommand = async (returnFunc: () => Promise, priority: boolean = false): Promise => { + return new Promise((resolve, reject) => { + requestQueue.enqueue( async (): Promise => { - await Wait(fakeWaitTime); + const status: SLStatus = await returnFunc(); - this.bs = true; + if (status !== SLStatus.OK) { + return status; + } - resolve(); - return SLStatus.OK; + resolve(123); + return status; }, reject, - ) - }) - - return this.bs; + priority, + ); + }); } -} - -let deferSpy; - -describe('Ember Request Queue', () => { - let requestQueue: EmberRequestQueue; beforeAll(async () => { jest.useFakeTimers(); @@ -72,32 +63,25 @@ describe('Ember Request Queue', () => { afterEach(() => { fakeWaitTime = 1000; varyingReturn = SLStatus.OK; + requestQueue.stopDispatching(); + requestQueue.clear(); + }); + + it('Creates queue with fallback dispatch interval', async () => { + const queue = new EmberRequestQueue(0); + + // @ts-expect-error private + expect(queue.dispatchInterval).toStrictEqual(5); }); it('Queues request and resolves it', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); varyingReturn = SLStatus.OK; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); - - if (status !== SLStatus.OK) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); + const p = mockAdapterCommand(getVaryingReturn); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); + const funcRejectSpy = jest.spyOn(getQueueEntryAt(0), 'reject'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -120,25 +104,9 @@ describe('Ember Request Queue', () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); varyingReturn = SLStatus.FAIL; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); - - if (status !== SLStatus.OK) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); + const p = mockAdapterCommand(getVaryingReturn); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); + const funcRejectSpy = jest.spyOn(getQueueEntryAt(0), 'reject'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -160,25 +128,12 @@ describe('Ember Request Queue', () => { it('Queues request, rejects it on throw, and removes it from queue', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getThrownError(); - - if (status !== SLStatus.OK) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); + const p = mockAdapterCommand(async (): Promise => { + await Wait(fakeWaitTime); + throw new EzspError(EzspStatus.ASH_ACK_TIMEOUT); }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); + const funcRejectSpy = jest.spyOn(getQueueEntryAt(0), 'reject'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -196,27 +151,38 @@ describe('Ember Request Queue', () => { expect(requestQueue.queue).toHaveLength(0);// no longer in queue }); - it('Queues request, defers on BUSY and defers again on NETWORK_DOWN', async () => { + it('Queues priority request, rejects it on throw, and removes it from queue', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = SLStatus.BUSY; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); + const p = mockAdapterCommand(async (): Promise => { + await Wait(fakeWaitTime); + throw new EzspError(EzspStatus.ASH_ACK_TIMEOUT); + }, true); + const funcSpy = jest.spyOn(getQueueEntryAt(0, true), 'func'); + const funcRejectSpy = jest.spyOn(getQueueEntryAt(0, true), 'reject'); - if (status !== SLStatus.OK) { - return status; - } + expect(enqueueSpy).toHaveBeenCalledTimes(1); + expect(funcSpy).toHaveBeenCalledTimes(0); + //@ts-expect-error private + expect(requestQueue.priorityQueue).toHaveLength(1); - resolve(123); - return status; - }, - reject, - ); - }); + requestQueue.startDispatching(); + + jest.advanceTimersByTime(fakeWaitTime + 20); + await expect(p).rejects.toStrictEqual(new EzspError(EzspStatus.ASH_ACK_TIMEOUT)); + + expect(funcSpy).toHaveBeenCalledTimes(1); + expect(funcRejectSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); + expect(requestQueue.priorityQueue).toHaveLength(0);// no longer in queue + }); + + it('Queues request, defers on BUSY and defers again on NETWORK_DOWN', async () => { + const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); + + varyingReturn = SLStatus.BUSY; + const p = mockAdapterCommand(getVaryingReturn); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -251,23 +217,8 @@ describe('Ember Request Queue', () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); varyingReturn = SLStatus.BUSY; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); - - if (status !== SLStatus.OK) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); + const p = mockAdapterCommand(getVaryingReturn); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -317,8 +268,7 @@ describe('Ember Request Queue', () => { reject, ); }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -348,23 +298,11 @@ describe('Ember Request Queue', () => { it('Queues request, defers on thrown BUSY', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getThrowNetworkBusy(); - - if (status !== SLStatus.OK) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); + const p = mockAdapterCommand(async (): Promise => { + await Wait(fakeWaitTime); + throw new EzspError(EzspStatus.NO_TX_SPACE); }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); expect(enqueueSpy).toHaveBeenCalledTimes(1); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -383,45 +321,40 @@ describe('Ember Request Queue', () => { await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); }); - it('Queues request and resolves by priority', async () => { + it('Queues request, defers on thrown NETWORK_DOWN', async () => { const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - varyingReturn = SLStatus.OK; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); + const p = mockAdapterCommand(async (): Promise => { + await Wait(fakeWaitTime); + throw new EzspError(EzspStatus.NOT_CONNECTED); + }); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); - if (status !== SLStatus.OK) { - return status; - } + expect(enqueueSpy).toHaveBeenCalledTimes(1); + expect(funcSpy).toHaveBeenCalledTimes(0); + //@ts-expect-error private + expect(requestQueue.queue).toHaveLength(1);// enqueued - resolve(123); - return status; - }, - reject, - ); - }); - const pPrio = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: SLStatus = await getVaryingReturn(); + requestQueue.startDispatching(); - if (status !== SLStatus.OK) { - return status; - } + await jest.advanceTimersByTimeAsync(fakeWaitTime + 20 + (BUSY_DEFER_MSEC * 0.25)); - resolve(456); - return status; - }, - reject, - true, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); + expect(deferSpy).toHaveBeenCalledTimes(1); + expect(funcSpy).toHaveBeenCalledTimes(1); //@ts-expect-error private - const funcPrioSpy = jest.spyOn(requestQueue.priorityQueue[0], 'func'); + expect(requestQueue.queue).toHaveLength(1);// still in queue + + await jest.advanceTimersByTimeAsync(BUSY_DEFER_MSEC + 20); + }); + + it('Queues request and resolves by priority', async () => { + const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); + + varyingReturn = SLStatus.OK; + const p = mockAdapterCommand(getVaryingReturn); + const pPrio = mockAdapterCommand(getVaryingReturn, true); + const funcSpy = jest.spyOn(getQueueEntryAt(0), 'func'); + const funcPrioSpy = jest.spyOn(getQueueEntryAt(0, true), 'func'); expect(enqueueSpy).toHaveBeenCalledTimes(2); expect(funcSpy).toHaveBeenCalledTimes(0); @@ -435,7 +368,7 @@ describe('Ember Request Queue', () => { await jest.advanceTimersByTimeAsync(fakeWaitTime + 1);// before 2nd setTimeout triggers - await expect(pPrio).resolves.toBe(456);// gives result of resolve + await expect(pPrio).resolves.toBe(123);// gives result of resolve expect(funcSpy).toHaveBeenCalledTimes(0);// enqueued func was not called expect(funcPrioSpy).toHaveBeenCalledTimes(1);// enqueued func was called @@ -453,16 +386,48 @@ describe('Ember Request Queue', () => { expect(requestQueue.queue).toHaveLength(0);// no longer in queue }); - it('In-class queues also work, just for kicks...', async () => { - const t = new TestThis(); + it('Clears queue', async () => { + const mockFunc = jest.fn(); + const mockReject = jest.fn(); + + requestQueue.enqueue(mockFunc, mockReject); + requestQueue.enqueue(mockFunc, mockReject); + requestQueue.enqueue(mockFunc, mockReject, true); + requestQueue.clear(); + expect(requestQueue.totalQueued).toStrictEqual(0); + expect(mockFunc).toHaveBeenCalledTimes(0); + expect(mockReject).toHaveBeenCalledTimes(0); + }); + + it('Detects when queue is high', () => { + const mockFunc = jest.fn(); + const mockReject = jest.fn(); + + requestQueue.enqueue(mockFunc, mockReject); + + expect(requestQueue.isHigh).toStrictEqual(false); + + for (let i = 0; i < HIGH_COUNT; i++) { + requestQueue.enqueue(mockFunc, mockReject); + } + + expect(requestQueue.isHigh).toStrictEqual(true); + + requestQueue.clear(); + + expect(requestQueue.isHigh).toStrictEqual(false); + }); + + it('Rejects after too many attempts', async () => { + varyingReturn = SLStatus.OK; + const p = mockAdapterCommand(getVaryingReturn); - expect(t.bs).toBeFalsy(); + getQueueEntryAt(0).sendAttempts = MAX_SEND_ATTEMPTS + 1; - const tBS = t.getNewBS(); + requestQueue.startDispatching(); - t.q.startDispatching(); jest.advanceTimersByTime(fakeWaitTime + 20); - await expect(tBS).resolves.toBeTruthy(); - }) + expect(p).rejects.toStrictEqual(new Error(`Failed ${MAX_SEND_ATTEMPTS} attempts to send`)); + }); }); From bd90b83ae470a566128b573d1c93e83d0d65a963 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:26:45 +0200 Subject: [PATCH 16/17] Cleanup tokens manager. --- src/adapter/ember/adapter/tokensManager.ts | 43 +++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/adapter/ember/adapter/tokensManager.ts b/src/adapter/ember/adapter/tokensManager.ts index 6735251c65..911f3b34d2 100644 --- a/src/adapter/ember/adapter/tokensManager.ts +++ b/src/adapter/ember/adapter/tokensManager.ts @@ -300,8 +300,8 @@ export class EmberTokensManager { * * @returns false if keys are in classic key storage, and true if they are located in PSA key storage. */ - private static async ncpUsesPSAKeyStorage(ezsp: Ezsp): Promise { - const [status, valueLength, value] = (await ezsp.ezspGetValue(EzspValueId.KEY_STORAGE_VERSION, 1)); + public static async ncpUsesPSAKeyStorage(ezsp: Ezsp): Promise { + const [status, valueLength, value] = await ezsp.ezspGetValue(EzspValueId.KEY_STORAGE_VERSION, 1); if ((status !== SLStatus.OK) || (valueLength < 1)) { throw new Error(`[TOKENS] Error retrieving key storage version, status=${SLStatus[status]}.`); @@ -315,7 +315,7 @@ export class EmberTokensManager { * @param nvm3Key * @returns */ - private static getCreatorFromNvm3Key(nvm3Key: number): number { + public static getCreatorFromNvm3Key(nvm3Key: number): number { for (let i = 0; i < NVM3KEYS.length; i++) { if (NVM3KEYS[i] === nvm3Key) { return CREATORS[i]; @@ -348,12 +348,12 @@ export class EmberTokensManager { const chunks: Buffer[] = [Buffer.from([tokenCount])];// 1 byte // returns 1 if NCP has secure key storage (where these tokens do not store the key data). // Don't compile for scripted test or any non-host code due to linker issues. - const hasSecureStorage: boolean = (await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp)); + const hasSecureStorage: boolean = await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp); logger.debug(`[TOKENS] Saving ${tokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`, NS); for (let i = 0; i < tokenCount; i++) { - const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); + const [tiStatus, tokenInfo] = await ezsp.ezspGetTokenInfo(i); let writeOffset: number = 0; if (tiStatus === SLStatus.OK) { @@ -364,7 +364,7 @@ export class EmberTokensManager { outputToken.writeUInt8(tokenInfo.arraySize, writeOffset++);// 1 byte for (let arrayIndex = 0; arrayIndex < tokenInfo.arraySize; arrayIndex++) { - const [tdStatus, tokenData] = (await ezsp.ezspGetTokenData(tokenInfo.nvm3Key, arrayIndex)); + const [tdStatus, tokenData] = await ezsp.ezspGetTokenData(tokenInfo.nvm3Key, arrayIndex); if (tdStatus === SLStatus.OK) { if (hasSecureStorage) { @@ -379,6 +379,7 @@ export class EmberTokensManager { ); } } + // logger.debug(`[TOKENS] TOKEN nvm3Key=${DEBUG_TOKEN_STRINGS[tokenInfo.nvm3Key]} size=${tokenInfo.size} ` // + `arraySize=${tokenInfo.arraySize} token=${tokenData.data.toString('hex')}`, NS); @@ -387,7 +388,7 @@ export class EmberTokensManager { // received from the API. Once it saves, during restore process the set token will // simply write the restoredEUI64 and the node will start to use that. if (tokenInfo.nvm3Key === NVM3KEY_STACK_RESTORED_EUI64 && tokenData.size === EUI64_SIZE - && (tokenData.data === BLANK_EUI64_BUF)) { + && (tokenData.data.equals(BLANK_EUI64_BUF))) { // Special case : Save the node EUI64 on the restoredEui64 token while saving. tokenData.data.set(localEui64); logger.debug(`[TOKENS] Saved node EUI64 in place of blank RESTORED EUI64.`, NS); @@ -434,12 +435,12 @@ export class EmberTokensManager { let readOffset: number = 0; const inTokenCount = inBuffer.readUInt8(readOffset++); - const hasSecureStorage: boolean = (await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp)); + const hasSecureStorage: boolean = await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp); logger.debug(`[TOKENS] Restoring ${inTokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`, NS); for (let i = 0; i < inTokenCount; i++) { - const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); + const [tiStatus, tokenInfo] = await ezsp.ezspGetTokenInfo(i); if (tiStatus === SLStatus.OK) { const nvm3Key = inBuffer.readUInt32LE(readOffset);// 4 bytes Token Key/Creator @@ -458,7 +459,7 @@ export class EmberTokensManager { await EmberTokensManager.restoreKeysFromData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex); } - const status = (await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData)); + const status = await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData); if (status !== SLStatus.OK) { logger.error(`[TOKENS] Failed to set token data for key "${nvm3Key}" with status=${SLStatus[status]}.`, NS); @@ -482,7 +483,7 @@ export class EmberTokensManager { * @param index uint8_t * @returns */ - private static async saveKeysToData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { + public static async saveKeysToData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { let status: SLStatus = SLStatus.OK; const context = initSecurityManagerContext(); let plaintextKey: SecManKey; @@ -496,7 +497,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 0; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 0);// at beginning } else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) { @@ -508,7 +509,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.NETWORK; context.keyIndex = 1; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 0);// at beginning } else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) { @@ -520,7 +521,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.TC_LINK; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 2 + EUI64_SIZE);// uint16_t+uint8_t[8] } else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) { @@ -531,7 +532,7 @@ export class EmberTokensManager { //this must be set to export a specific link key from table context.flags |= SecManFlag.KEY_INDEX_IS_VALID; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, KEY_ENTRY_KEY_DATA_OFFSET);// end part of uint8_t[25] } else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) { @@ -553,7 +554,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY; context.keyIndex = index; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 1 + 4 + 8 + 1 + 1);// uint8_t+uint32_t+uint8_t[8]+uint8_t+uint8_t } else if (nvm3Key === NVM3KEY_STACK_GP_SINK_TABLE) { @@ -576,7 +577,7 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.GREEN_POWER_SINK_TABLE_KEY; context.keyIndex = index; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 1 + 2 + 8 + 1 + 1);// uint8_t+uint16_t+uint8_t[8]+uint8_t+uint8_t } else if (nvm3Key === NVM3KEY_STACK_ZLL_SECURITY) { @@ -589,13 +590,13 @@ export class EmberTokensManager { context.coreKeyType = SecManKeyType.ZLL_ENCRYPTION_KEY; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 4 + 1);// uint32_t+uint8_t context.coreKeyType = SecManKeyType.ZLL_PRECONFIGURED_KEY; - [status, plaintextKey] = (await ezsp.ezspExportKey(context)); + [status, plaintextKey] = await ezsp.ezspExportKey(context); tokenData.data.set(plaintextKey.contents, 4 + 1 + EMBER_ENCRYPTION_KEY_SIZE);// uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE] } else { @@ -614,7 +615,7 @@ export class EmberTokensManager { * * @from sli_zigbee_af_trust_center_backup_restore_keys_from_data */ - private static async restoreKeysFromData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { + public static async restoreKeysFromData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { let status: SLStatus = SLStatus.OK; const context = initSecurityManagerContext(); @@ -765,7 +766,7 @@ export class EmberTokensManager { }; const creator = EmberTokensManager.getCreatorFromNvm3Key(nvm3Key);// uint16_t - const status = (await ezsp.ezspSetTokenData(creator, arrayIndex, tokenData)); + const status = await ezsp.ezspSetTokenData(creator, arrayIndex, tokenData); if (status !== SLStatus.OK) { logger.error( From e9f7aa4914aae11622dc3dcea62061bd0a5498cc Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:51:28 +0200 Subject: [PATCH 17/17] Fix missing zdo event. Add typing for `EventEmitter`s. Cleanup. --- src/adapter/ember/adapter/emberAdapter.ts | 74 +++++------------ src/adapter/ember/ezsp/buffalo.ts | 24 +++--- src/adapter/ember/ezsp/ezsp.ts | 97 ++++++++++++++--------- src/adapter/ember/types.ts | 2 +- src/adapter/ember/uart/ash.ts | 25 +++--- 5 files changed, 109 insertions(+), 113 deletions(-) diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index 63572b9089..f3282c64d3 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -242,8 +242,8 @@ const DEFAULT_STACK_CONFIG: Readonly = { END_DEVICE_POLL_TIMEOUT: 8,// zigpc: 8 TRANSIENT_KEY_TIMEOUT_S: 300,// zigpc: 65535 }; -/** Default behavior is to disable app key requests, so reduce key table to zero as well */ -const DEFAULT_KEY_TABLE_SIZE = 0; +/** Default behavior is to disable app key requests */ +const ALLOW_APP_KEY_REQUESTS = false; /** * NOTE: This from SDK is currently ignored here because of issues in below links: @@ -320,15 +320,13 @@ export class EmberAdapter extends Adapter { this.ezsp = new Ezsp(serialPortOptions); - this.ezsp.on(EzspEvents.STACK_STATUS, this.onStackStatus.bind(this)); - this.ezsp.on(EzspEvents.MESSAGE_SENT, this.onMessageSent.bind(this)); this.ezsp.on(EzspEvents.ZDO_RESPONSE, this.onZDOResponse.bind(this)); - this.ezsp.on(EzspEvents.END_DEVICE_ANNOUNCE, this.onEndDeviceAnnounce.bind(this)); this.ezsp.on(EzspEvents.INCOMING_MESSAGE, this.onIncomingMessage.bind(this)); this.ezsp.on(EzspEvents.TOUCHLINK_MESSAGE, this.onTouchlinkMessage.bind(this)); - this.ezsp.on(EzspEvents.GREENPOWER_MESSAGE, this.onGreenpowerMessage.bind(this)); - + this.ezsp.on(EzspEvents.STACK_STATUS, this.onStackStatus.bind(this)); this.ezsp.on(EzspEvents.TRUST_CENTER_JOIN, this.onTrustCenterJoin.bind(this)); + this.ezsp.on(EzspEvents.MESSAGE_SENT, this.onMessageSent.bind(this)); + this.ezsp.on(EzspEvents.GREENPOWER_MESSAGE, this.onGreenpowerMessage.bind(this)); } private loadStackConfig(): StackConfig { @@ -535,29 +533,17 @@ export class EmberAdapter extends Adapter { networkAddress: (payload as ZdoTypes.NetworkAddressResponse).nwkAddress, ieeeAddr: (payload as ZdoTypes.NetworkAddressResponse).eui64 } as NetworkAddressPayload); + } else if (apsFrame.clusterId === Zdo.ClusterId.END_DEVICE_ANNOUNCE) { + this.emit(Events.deviceAnnounce, { + networkAddress: (payload as ZdoTypes.EndDeviceAnnounce).nwkAddress, + ieeeAddr: (payload as ZdoTypes.EndDeviceAnnounce).eui64 + } as DeviceAnnouncePayload); } } catch (error) { this.oneWaitress.resolveZDO(sender, apsFrame, error); } } - /** - * Emitted from @see Ezsp.ezspIncomingMessageHandler - * - * @param sender - * @param nodeId - * @param eui64 - * @param macCapFlags - */ - private async onEndDeviceAnnounce(sender: NodeId, apsFrame: EmberApsFrame, payload: ZdoTypes.EndDeviceAnnounce): Promise { - // reduced function device - // if ((payload.capabilities.deviceType === 0)) { - - // } - - this.emit(Events.deviceAnnounce, {networkAddress: payload.nwkAddress, ieeeAddr: payload.eui64} as DeviceAnnouncePayload); - } - /** * Emitted from @see Ezsp.ezspIncomingMessageHandler * @@ -801,8 +787,6 @@ export class EmberAdapter extends Adapter { // after network UP, as per SDK, ensures clean slate await this.initNCPConcentrator(); - // await (this.emberStartEnergyScan());// TODO: via config of some kind, better off waiting for UI supports though - // populate network cache info const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); @@ -927,11 +911,11 @@ export class EmberAdapter extends Adapter { + `with status=${SLStatus[status]}.`); } - const appKeyPolicy = DEFAULT_KEY_TABLE_SIZE > 0 ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS; - status = (await this.emberSetEzspPolicy(EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyPolicy)); + const appKeyRequestsPolicy = ALLOW_APP_KEY_REQUESTS ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS; + status = await this.emberSetEzspPolicy(EzspPolicyId.APP_KEY_REQUEST_POLICY, appKeyRequestsPolicy); if (status !== SLStatus.OK) { - throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${EzspDecisionId[appKeyPolicy]} ` + throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to ${EzspDecisionId[appKeyRequestsPolicy]} ` + `with status=${SLStatus[status]}.`); } @@ -966,7 +950,6 @@ export class EmberAdapter extends Adapter { const [npStatus, nodeType, netParams] = (await this.ezsp.ezspGetNetworkParameters()); - logger.debug(`[INIT TC] Current network config=${JSON.stringify(this.networkOptions)}`, NS); logger.debug(`[INIT TC] Current adapter network: nodeType=${EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`, NS); if ((npStatus === SLStatus.OK) && (nodeType === EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId) @@ -981,8 +964,6 @@ export class EmberAdapter extends Adapter { throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); } - logger.debug(`[INIT TC] Current adapter network: networkKey=${networkKey.contents.toString('hex')}`, NS); - // config doesn't match adapter anymore if (!networkKey.contents.equals(configNetworkKey)) { action = NetworkInitAction.LEAVE; @@ -1044,13 +1025,13 @@ export class EmberAdapter extends Adapter { logger.info(`[INIT TC] Forming from backup.`, NS); const keyList: LinkKeyBackupData[] = backup.devices.map((device) => { const octets = Array.from(device.ieeeAddress.reverse()); - const key: LinkKeyBackupData = { + + return { deviceEui64: `0x${octets.map(octet => octet.toString(16).padStart(2, '0')).join('')}`, key: {contents: device.linkKey.key}, outgoingFrameCounter: device.linkKey.txCounter, incomingFrameCounter: device.linkKey.rxCounter, }; - return key; }); // before forming @@ -1152,11 +1133,11 @@ export class EmberAdapter extends Adapter { throw new Error(`[INIT FORM] Failed to set extended security bitmask to ${extended} with status=${SLStatus[status]}.`); } - if (!fromBackup && DEFAULT_KEY_TABLE_SIZE > 0) { + if (!fromBackup) { status = await this.ezsp.ezspClearKeyTable(); if (status !== SLStatus.OK) { - throw new Error(`[INIT FORM] Failed to clear key table with status=${SLStatus[status]}.`); + logger.error(`[INIT FORM] Failed to clear key table with status=${SLStatus[status]}.`, NS); } } @@ -1305,12 +1286,9 @@ export class EmberAdapter extends Adapter { let status: SLStatus; for (let i = 0; i < keyTableSize; i++) { - if (i >= backupData.length) { - // erase any key index not present in backup but available on the NCP - status = (await this.ezsp.ezspEraseKeyTableEntry(i)); - } else { - status = (await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key)); - } + // erase any key index not present in backup but available on the NCP + status = (i >= backupData.length) ? await this.ezsp.ezspEraseKeyTableEntry(i) : + await this.ezsp.ezspImportLinkKey(i, backupData[i].deviceEui64, backupData[i].key); if (status !== SLStatus.OK) { throw new Error(`[BACKUP] Failed to ${((i >= backupData.length) ? "erase" : "set")} key table entry at index ${i} ` @@ -1976,17 +1954,7 @@ export class EmberAdapter extends Adapter { throw new Error(`[BACKUP] No network key set.`); } - let keyList: LinkKeyBackupData[] = []; - - if (DEFAULT_KEY_TABLE_SIZE > 0) { - keyList = (await this.exportLinkKeys()); - } - - // XXX: this only makes sense on stop (if that), not hourly/on start, plus network needs to be at near-standstill @see AN1387 - // const tokensBuf = (await EmberTokensManager.saveTokens( - // this.ezsp, - // Buffer.from(this.networkCache.eui64.substring(2/*0x*/), 'hex').reverse() - // )); + const keyList: LinkKeyBackupData[] = ALLOW_APP_KEY_REQUESTS ? await this.exportLinkKeys() : []; let context: SecManContext = initSecurityManagerContext(); context.coreKeyType = SecManKeyType.TC_LINK; diff --git a/src/adapter/ember/ezsp/buffalo.ts b/src/adapter/ember/ezsp/buffalo.ts index 74c1cf2c3e..bdc1b2e718 100644 --- a/src/adapter/ember/ezsp/buffalo.ts +++ b/src/adapter/ember/ezsp/buffalo.ts @@ -499,8 +499,8 @@ export class EzspBuffalo extends Buffalo { } public readSecManNetworkKeyInfo(): SecManNetworkKeyInfo { - const networkKeySet = this.readUInt8() === 1 ? true : false; - const alternateNetworkKeySet = this.readUInt8() === 1 ? true : false; + const networkKeySet = this.readUInt8() !== 0; + const alternateNetworkKeySet = this.readUInt8() !== 0; const networkKeySequenceNumber = this.readUInt8(); const altNetworkKeySequenceNumber = this.readUInt8(); const networkKeyFrameCounter = this.readUInt32(); @@ -632,7 +632,7 @@ export class EzspBuffalo extends Buffalo { const channel = this.readUInt8(); const panId = this.readUInt16(); const extendedPanId = this.readListUInt8(EXTENDED_PAN_ID_SIZE); - const allowingJoin = this.readUInt8(); + const allowingJoin = this.readUInt8() !== 0; const stackProfile = this.readUInt8(); const nwkUpdateId = this.readUInt8(); @@ -650,7 +650,7 @@ export class EzspBuffalo extends Buffalo { this.writeUInt8(value.channel); this.writeUInt16(value.panId); this.writeListUInt8(value.extendedPanId); - this.writeUInt8(value.allowingJoin); + this.writeUInt8(value.allowingJoin ? 1 : 0); this.writeUInt8(value.stackProfile); this.writeUInt8(value.nwkUpdateId); } @@ -1186,9 +1186,9 @@ export class EzspBuffalo extends Buffalo { const nwkUpdateId = this.readUInt8(); const power = this.readUInt8(); const parentPriority = this.readUInt8(); - const enhanced = this.readUInt8() === 1 ? true : false; - const permitJoin = this.readUInt8() === 1 ? true : false; - const hasCapacity = this.readUInt8() === 1 ? true : false; + const enhanced = this.readUInt8() !== 0; + const permitJoin = this.readUInt8() !== 0; + const hasCapacity = this.readUInt8() !== 0; const panId = this.readUInt16(); const sender = this.readUInt16(); const extendedPanId = this.readListUInt8(EXTENDED_PAN_ID_SIZE); @@ -1245,9 +1245,9 @@ export class EzspBuffalo extends Buffalo { const nwkUpdateId = this.readUInt8(); const power = this.readUInt8(); const parentPriority = this.readUInt8(); - const enhanced = this.readUInt8() === 1 ? true : false; - const permitJoin = this.readUInt8() === 1 ? true : false; - const hasCapacity = this.readUInt8() === 1 ? true : false; + const enhanced = this.readUInt8() !== 0; + const permitJoin = this.readUInt8() !== 0; + const hasCapacity = this.readUInt8() !== 0; const panId = this.readUInt16(); const sender = this.readUInt16(); const extendedPanId = this.readListUInt8(EXTENDED_PAN_ID_SIZE); @@ -1290,8 +1290,8 @@ export class EzspBuffalo extends Buffalo { public readEmberTokenInfo(): EmberTokenInfo { const nvm3Key = this.readUInt32(); - const isCnt = this.readUInt8() === 1 ? true : false; - const isIdx = this.readUInt8() === 1 ? true : false; + const isCnt = this.readUInt8() !== 0; + const isIdx = this.readUInt8() !== 0; const size = this.readUInt8(); const arraySize = this.readUInt8(); diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts index 8cd9c482a6..de407429c6 100644 --- a/src/adapter/ember/ezsp/ezsp.ts +++ b/src/adapter/ember/ezsp/ezsp.ts @@ -181,38 +181,61 @@ const ZA_MAX_HOPS = 12; */ const MESSAGE_TAG_MASK = 0x7F; -/* eslint-disable max-len */ export enum EzspEvents { - //-- App logic + //-- An error was detected that requires resetting the NCP. NCP_NEEDS_RESET_AND_INIT = 'NCP_NEEDS_RESET_AND_INIT', //-- ezspIncomingMessageHandler - /** params => apsFrame: EmberApsFrame, sender: NodeId, messageContents: Buffer */ ZDO_RESPONSE = 'ZDO_RESPONSE', - /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: NodeId, messageContents: Buffer */ INCOMING_MESSAGE = 'INCOMING_MESSAGE', - /** params => sourcePanId: PanId, sourceAddress: EUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer */ + //-- ezspMacFilterMatchMessageHandler TOUCHLINK_MESSAGE = 'TOUCHLINK_MESSAGE', - /** params => sender: NodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload */ - END_DEVICE_ANNOUNCE = 'END_DEVICE_ANNOUNCE', - //-- ezspStackStatusHandler - /** params => status: SLStatus */ STACK_STATUS = 'STACK_STATUS', - //-- ezspTrustCenterJoinHandler - /** params => newNodeId: NodeId, newNodeEui64: EUI64, status: EmberDeviceUpdate, policyDecision: EmberJoinDecision, parentOfNewNodeId: NodeId */ TRUST_CENTER_JOIN = 'TRUST_CENTER_JOIN', - //-- ezspMessageSentHandler - /** params => status: SLStatus, type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number */ MESSAGE_SENT = 'MESSAGE_SENT', - //-- ezspGpepIncomingMessageHandler - /** params => sequenceNumber: number, commandIdentifier: number, sourceId: number, frameCounter: number, gpdCommandId: number, gpdCommandPayload: Buffer, gpdLink: number */ GREENPOWER_MESSAGE = 'GREENPOWER_MESSAGE', } -/* eslint-enable max-len */ + +interface EzspEventMap { + [EzspEvents.NCP_NEEDS_RESET_AND_INIT]: [status: EzspStatus], + [EzspEvents.ZDO_RESPONSE]: [apsFrame: EmberApsFrame, sender: NodeId, messageContents: Buffer], + [EzspEvents.INCOMING_MESSAGE]: [ + type: EmberIncomingMessageType, + apsFrame: EmberApsFrame, + lastHopLqi: number, + sender: NodeId, + messageContents: Buffer + ], + [EzspEvents.TOUCHLINK_MESSAGE]: [sourcePanId: PanId, sourceAddress: EUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer], + [EzspEvents.STACK_STATUS]: [status: SLStatus], + [EzspEvents.TRUST_CENTER_JOIN]: [ + newNodeId: NodeId, + newNodeEui64: EUI64, + status: EmberDeviceUpdate, + policyDecision: EmberJoinDecision, + parentOfNewNodeId: NodeId + ], + [EzspEvents.MESSAGE_SENT]: [ + status: SLStatus, + type: EmberOutgoingMessageType, + indexOrDestination: number, + apsFrame: EmberApsFrame, + messageTag: number + ], + [EzspEvents.GREENPOWER_MESSAGE]: [ + sequenceNumber: number, + commandIdentifier: number, + sourceId: number, + frameCounter: number, + gpdCommandId: number, + gpdCommandPayload: Buffer, + gpdLink: number + ], +} /** * Host EZSP layer. @@ -224,10 +247,8 @@ export enum EzspEvents { * Callers are expected to handle errors appropriately. * - They will throw `EzspStatus` if `sendCommand` fails or the returned value(s) by NCP are invalid (wrong length, etc). * - Most will return `EmberStatus` given by NCP (some `EzspStatus`, some `SLStatus`...). - * - * @event 'NCP_NEEDS_RESET_AND_INIT(EzspStatus)' An error was detected that requires resetting the NCP. */ -export class Ezsp extends EventEmitter { +export class Ezsp extends EventEmitter { private version: number; private readonly tickInterval: number; public readonly ash: UartAsh; @@ -249,10 +270,8 @@ export class Ezsp extends EventEmitter { private frameSequence: number; /** Sequence used for EZSP send() tagging. static uint8_t */ private sendSequence: number; - /** If if a command is currently waiting for a response. Used to manage async CBs vs command responses */ - private waitingForResponse: boolean; - /** Awaiting response resolve/timer struct. If waitingForResponse is not true, this should not be used. */ - private responseWaiter: EzspWaiter; + /** Awaiting response resolve/timer struct. Null if not waiting for response. */ + private responseWaiter: EzspWaiter | null; /** Counter for Queue Full errors */ public counterErrQueueFull: number; @@ -294,7 +313,7 @@ export class Ezsp extends EventEmitter { } private initVariables(): void { - if (this.waitingForResponse) { + if (this.responseWaiter != null) { clearTimeout(this.responseWaiter.timer); } @@ -311,7 +330,6 @@ export class Ezsp extends EventEmitter { this.sendingCommand = false; this.frameSequence = -1;// start at 0 this.sendSequence = 0;// start at 1 - this.waitingForResponse = false; this.responseWaiter = null; this.counterErrQueueFull = 0; } @@ -402,7 +420,8 @@ export class Ezsp extends EventEmitter { // logger.debug(`<<<< ${buffer.data.subarray(0, buffer.len).toString('hex')}`, NS); if (buffer.data[EZSP_FRAME_CONTROL_INDEX] & EZSP_FRAME_CONTROL_ASYNCH_CB) { - buffer.data.copy(this.callbackFrameContents, 0, 0, buffer.len);// take only what len tells us is actual content + // take only what len tells us is actual content + buffer.data.copy(this.callbackFrameContents, 0, 0, buffer.len); this.callbackFrameLength = buffer.len; @@ -418,18 +437,25 @@ export class Ezsp extends EventEmitter { logger.debug(`<=x= ${this.callbackFrameToString} Invalid, status=${EzspStatus[status]}.`, NS); } } else { - buffer.data.copy(this.frameContents, 0, 0, buffer.len);// take only what len tells us is actual content + // take only what len tells us is actual content + buffer.data.copy(this.frameContents, 0, 0, buffer.len); this.frameLength = buffer.len; logger.debug(`<=== ${this.frameToString}`, NS); - this.ash.rxFree.freeBuffer(buffer); + this.ash.rxFree.freeBuffer(buffer);// always - const status = this.validateReceivedFrame(this.buffalo); + if (this.responseWaiter != null) { + const status = this.validateReceivedFrame(this.buffalo); - clearTimeout(this.responseWaiter.timer); - this.responseWaiter.resolve(status); + clearTimeout(this.responseWaiter.timer); + this.responseWaiter.resolve(status); + + this.responseWaiter = null;// done, gc + } else { + logger.debug(`Received response while not expecting one. Ignoring.`, NS); + } } } @@ -552,7 +578,6 @@ export class Ezsp extends EventEmitter { } this.frameLength = length; - let status: EzspStatus; logger.debug(`===> ${this.frameToString}`, NS); @@ -562,13 +587,11 @@ export class Ezsp extends EventEmitter { const sendStatus = (this.ash.send(this.frameLength, this.frameContents)); if (sendStatus !== EzspStatus.SUCCESS) { - reject(new EzspError(sendStatus)); + return reject(new EzspError(sendStatus)); } this.responseWaiter = { - timer: setTimeout(() => { - reject(new EzspError(EzspStatus.ASH_ERROR_TIMEOUTS)); - }, this.ash.responseTimeout), + timer: setTimeout(() => reject(new EzspError(EzspStatus.ASH_ERROR_TIMEOUTS)), this.ash.responseTimeout), resolve, }; }); @@ -577,6 +600,8 @@ export class Ezsp extends EventEmitter { throw new EzspError(status); } } catch (error) { + this.responseWaiter = null; + logger.debug(`=x=> ${this.frameToString} ${error}`, NS); this.ezspErrorHandler(status); diff --git a/src/adapter/ember/types.ts b/src/adapter/ember/types.ts index c40c07120e..fcc255f959 100644 --- a/src/adapter/ember/types.ts +++ b/src/adapter/ember/types.ts @@ -559,7 +559,7 @@ export type EmberZigbeeNetwork = { /** uint8_t */ channel: number, /** bool */ - allowingJoin: number, + allowingJoin: boolean, /** uint8_t[EXTENDED_PAN_ID_SIZE] */ extendedPanId: ExtendedPanId, /** uint8_t */ diff --git a/src/adapter/ember/uart/ash.ts b/src/adapter/ember/uart/ash.ts index 0058d671a5..a48b07b328 100644 --- a/src/adapter/ember/uart/ash.ts +++ b/src/adapter/ember/uart/ash.ts @@ -59,14 +59,6 @@ const ashGetFrmNum = (ctrl: number): number => ((ctrl & ASH_FRMNUM_MASK) >> ASH_ /** ASH get acknum in control byte */ const ashGetACKNum = (ctrl: number): number => ((ctrl & ASH_ACKNUM_MASK) >> ASH_ACKNUM_BIT); - -export enum AshEvents { - /** When the ASH protocol detects a fatal error (bubbles up to restart adapter). */ - FATAL_ERROR = 'fatalError', - /** When a frame has been parsed and queued in the rxQueue. */ - FRAME = 'frame', -} - type UartAshCounters = { /** DATA frame data fields bytes transmitted */ txData: number, @@ -159,7 +151,6 @@ enum Flag { NRTX = 0x200, } - /** max frames sent without being ACKed (1-7) */ const CONFIG_TX_K = 3; /** enables randomizing DATA frame payloads */ @@ -183,10 +174,22 @@ const CONFIG_NR_TIME = 480; /** Read/write max bytes count at stream level */ const CONFIG_HIGHWATER_MARK = 256; +export enum AshEvents { + /** When the ASH protocol detects a fatal error (bubbles up to restart adapter). */ + FATAL_ERROR = 'fatalError', + /** When a frame has been parsed and queued in the rxQueue. */ + FRAME = 'frame', +} + +interface UartAshEventMap { + [AshEvents.FATAL_ERROR]: [status: EzspStatus]; + [AshEvents.FRAME]: []; +} + /** * ASH Protocol handler. */ -export class UartAsh extends EventEmitter { +export class UartAsh extends EventEmitter { private readonly portOptions: SerialPortOptions; private serialPort: SerialPort; private socketPort: Socket; @@ -1179,7 +1182,7 @@ export class UartAsh extends EventEmitter { this.counters.rxData += this.rxDataBuffer.len; - setImmediate(this.emit.bind(this, AshEvents.FRAME)); + setImmediate(() => this.emit(AshEvents.FRAME)); return EzspStatus.SUCCESS; } else { // frame is out of sequence