From 4f16895e61b2443f00abe3319094667d99b3e910 Mon Sep 17 00:00:00 2001 From: koenkk Date: Sun, 18 Feb 2024 21:53:33 +0100 Subject: [PATCH] Revert "feat: Add new `ember` adapter implementation, targeting EZSP 13 and above (#918)" This reverts commit c36d051e0a0e20654ff716b17cde783e5b3a4989. --- src/adapter/adapter.ts | 5 +- src/adapter/ember/adapter/emberAdapter.ts | 3982 ---------- src/adapter/ember/adapter/endpoints.ts | 80 - src/adapter/ember/adapter/index.ts | 3 - src/adapter/ember/adapter/oneWaitress.ts | 299 - src/adapter/ember/adapter/requestQueue.ts | 160 - src/adapter/ember/adapter/tokensManager.ts | 780 -- src/adapter/ember/consts.ts | 290 - src/adapter/ember/enums.ts | 2423 ------ src/adapter/ember/ezsp/buffalo.ts | 1269 ---- src/adapter/ember/ezsp/consts.ts | 148 - src/adapter/ember/ezsp/enums.ts | 958 --- src/adapter/ember/ezsp/ezsp.ts | 7969 -------------------- src/adapter/ember/types.ts | 812 -- src/adapter/ember/uart/ash.ts | 1882 ----- src/adapter/ember/uart/consts.ts | 115 - src/adapter/ember/uart/enums.ts | 192 - src/adapter/ember/uart/parser.ts | 46 - src/adapter/ember/uart/queues.ts | 243 - src/adapter/ember/uart/writer.ts | 52 - src/adapter/ember/utils/initters.ts | 73 - src/adapter/ember/utils/math.ts | 96 - src/adapter/ember/zdo.ts | 1034 --- src/adapter/tstype.ts | 2 +- src/utils/backup.ts | 4 - test/adapter/ember/ash.test.ts | 347 - test/adapter/ember/consts.ts | 46 - test/adapter/ember/ezspBuffalo.test.ts | 48 - test/adapter/ember/math.test.ts | 183 - test/adapter/ember/requestQueue.test.ts | 613 -- test/controller.test.ts | 2 +- 31 files changed, 4 insertions(+), 24152 deletions(-) delete mode 100644 src/adapter/ember/adapter/emberAdapter.ts delete mode 100644 src/adapter/ember/adapter/endpoints.ts delete mode 100644 src/adapter/ember/adapter/index.ts delete mode 100644 src/adapter/ember/adapter/oneWaitress.ts delete mode 100644 src/adapter/ember/adapter/requestQueue.ts delete mode 100644 src/adapter/ember/adapter/tokensManager.ts delete mode 100644 src/adapter/ember/consts.ts delete mode 100644 src/adapter/ember/enums.ts delete mode 100644 src/adapter/ember/ezsp/buffalo.ts delete mode 100644 src/adapter/ember/ezsp/consts.ts delete mode 100644 src/adapter/ember/ezsp/enums.ts delete mode 100644 src/adapter/ember/ezsp/ezsp.ts delete mode 100644 src/adapter/ember/types.ts delete mode 100644 src/adapter/ember/uart/ash.ts delete mode 100644 src/adapter/ember/uart/consts.ts delete mode 100644 src/adapter/ember/uart/enums.ts delete mode 100644 src/adapter/ember/uart/parser.ts delete mode 100644 src/adapter/ember/uart/queues.ts delete mode 100644 src/adapter/ember/uart/writer.ts delete mode 100644 src/adapter/ember/utils/initters.ts delete mode 100644 src/adapter/ember/utils/math.ts delete mode 100644 src/adapter/ember/zdo.ts delete mode 100644 test/adapter/ember/ash.test.ts delete mode 100644 test/adapter/ember/consts.ts delete mode 100644 test/adapter/ember/ezspBuffalo.test.ts delete mode 100644 test/adapter/ember/math.test.ts delete mode 100644 test/adapter/ember/requestQueue.test.ts diff --git a/src/adapter/adapter.ts b/src/adapter/adapter.ts index d918c08e534..3444de0978b 100644 --- a/src/adapter/adapter.ts +++ b/src/adapter/adapter.ts @@ -44,13 +44,12 @@ abstract class Adapter extends events.EventEmitter { const {DeconzAdapter} = await import('./deconz/adapter'); const {ZiGateAdapter} = await import('./zigate/adapter'); const {EZSPAdapter} = await import('./ezsp/adapter'); - const {EmberAdapter} = await import('./ember/adapter'); type AdapterImplementation = (typeof ZStackAdapter | typeof DeconzAdapter | typeof ZiGateAdapter - | typeof EZSPAdapter | typeof EmberAdapter); + | typeof EZSPAdapter); let adapters: AdapterImplementation[]; const adapterLookup = {zstack: ZStackAdapter, deconz: DeconzAdapter, zigate: ZiGateAdapter, - ezsp: EZSPAdapter, ember: EmberAdapter}; + ezsp: EZSPAdapter}; if (serialPortOptions.adapter && serialPortOptions.adapter !== 'auto') { if (adapterLookup.hasOwnProperty(serialPortOptions.adapter)) { adapters = [adapterLookup[serialPortOptions.adapter]]; diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts deleted file mode 100644 index 6d5a97748a0..00000000000 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ /dev/null @@ -1,3982 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import equals from 'fast-deep-equal/es6'; -import {fs} from "mz"; -import SerialPortUtils from '../../serialPortUtils'; -import SocketPortUtils from '../../socketPortUtils'; -import {BackupUtils, RealpathSync, Wait} from "../../../utils"; -import {Adapter, TsType} from "../.."; -import {LoggerStub} from "../../../controller/logger-stub"; -import {Backup, UnifiedBackupStorage} from "../../../models"; -import {FrameType, Direction, ZclFrame, Foundation} from "../../../zcl"; -import Cluster from "../../../zcl/definition/cluster"; -import { - DeviceAnnouncePayload, - DeviceJoinedPayload, - DeviceLeavePayload, - Events, - RawDataPayload, - ZclDataPayload -} from "../../events"; -import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from "../utils/math"; -import {Ezsp, EzspEvents} from "../ezsp/ezsp"; -import { - EMBER_ENCRYPTION_KEY_SIZE, - EUI64_SIZE, - EZSP_MAX_FRAME_LENGTH, - EZSP_PROTOCOL_VERSION, - EZSP_STACK_TYPE_MESH -} from "../ezsp/consts"; -import { - EzspConfigId, - EzspDecisionBitmask, - EzspDecisionId, - EzspPolicyId, - EzspValueId -} from "../ezsp/enums"; -import {EzspBuffalo} from "../ezsp/buffalo"; -import { - EmberApsOption, - EmberOutgoingMessageType, - EmberStatus, - EzspStatus, - EmberVersionType, - SLStatus, - SecManFlag, - EmberNodeType, - EmberNetworkStatus, - SecManKeyType, - EmberLeaveRequestFlags, - EmberInterpanMessageType, - EmberSourceRouteDiscoveryMode, - EmberTXPowerMode, - EmberKeepAliveMode, - EmberJoinDecision, - EmberExtendedSecurityBitmask, - EmberInitialSecurityBitmask, - EmberJoinMethod, - EmberNetworkInitBitmask, - EmberDeviceUpdate, - EzspNetworkScanType, - EmberIncomingMessageType, - EmberCounterType, -} from "../enums"; -import { - EmberAesMmoHashContext, - EmberApsFrame, - EmberEUI64, - EmberExtendedPanId, - EmberInitialSecurityState, - EmberKeyData, - EmberMulticastId, - EmberMulticastTableEntry, - EmberNetworkInitStruct, - EmberNetworkParameters, - EmberNodeId, - EmberPanId, - EmberVersion, - 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 -} from "../zdo"; -import { - EMBER_BROADCAST_ADDRESS, - EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, - EMBER_SLEEPY_BROADCAST_ADDRESS, - EMBER_INSTALL_CODE_CRC_SIZE, - EMBER_INSTALL_CODE_SIZES, - MANUFACTURER_CODE, - EMBER_NUM_802_15_4_CHANNELS, - EMBER_MIN_802_15_4_CHANNEL_NUMBER, - ZIGBEE_COORDINATOR_ADDRESS, - UNKNOWN_NETWORK_STATE, - EMBER_UNKNOWN_NODE_ID, - MAXIMUM_APS_PAYLOAD_LENGTH, - APS_ENCRYPTION_OVERHEAD, - APS_FRAGMENTATION_OVERHEAD, - INVALID_PAN_ID, - LONG_DEST_FRAME_CONTROL, - MAC_ACK_REQUIRED, - MAXIMUM_INTERPAN_LENGTH, - STUB_NWK_FRAME_CONTROL, - TOUCHLINK_PROFILE_ID, - INTERPAN_APS_FRAME_TYPE, - SHORT_DEST_FRAME_CONTROL, - EMBER_HIGH_RAM_CONCENTRATOR, - BLANK_EUI64, - STACK_PROFILE_ZIGBEE_PRO, - SECURITY_LEVEL_Z3, - INVALID_RADIO_CHANNEL, - BLANK_EXTENDED_PAN_ID, - GP_ENDPOINT, - EMBER_ALL_802_15_4_CHANNELS_MASK, - ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY, - HA_PROFILE_ID, -} from "../consts"; -import {EmberRequestQueue} from "./requestQueue"; -import {FIXED_ENDPOINTS} from "./endpoints"; -import {aesMmoHashInit, initNetworkCache, initSecurityManagerContext} from "../utils/initters"; -import {randomBytes} from "crypto"; -import {EmberOneWaitress} from "./oneWaitress"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import {EmberTokensManager} from "./tokensManager"; - -const debug = Debug('zigbee-herdsman:adapter:ember:adapter'); - -export type NetworkCache = { - //-- basic network info - eui64: EmberEUI64, - parameters: EmberNetworkParameters, - status: EmberNetworkStatus, - /** uint8_t */ -}; - -/** - * - */ -type ConcentratorConfig = { - /** - * Minimum Time between broadcasts (in seconds) <1-60> - * Default: 10 - * The minimum amount of time that must pass between MTORR broadcasts. - */ - minTime: number, - /** - * Maximum Time between broadcasts (in seconds) <30-300> - * Default: 60 - * The maximum amount of time that can pass between MTORR broadcasts. - */ - maxTime: number, - /** - * Route Error Threshold <1-100> - * Default: 3 - * The number of route errors that will trigger a re-broadcast of the MTORR. - */ - routeErrorThreshold: number, - /** - * Delivery Failure Threshold <1-100> - * Default: 1 - * The number of APS delivery failures that will trigger a re-broadcast of the MTORR. - */ - deliveryFailureThreshold: number, - /** - * Maximum number of hops for Broadcast <0-30> - * Default: 0 - * 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. - */ - mapHops: number, -}; - -/** - * Use for a link key backup. - * - * Each entry notes the EUI64 of the device it is paired to and the key data. - * This key may be hashed and not the actual link key currently in use. - */ -type LinkKeyBackupData = { - deviceEui64: EmberEUI64, - key: EmberKeyData, - outgoingFrameCounter: number, - incomingFrameCounter: number, -}; - -/** Enum to pass strings from numbers up to Z2M. */ -enum RoutingTableStatus { - ACTIVE = 0x0, - DISCOVERY_UNDERWAY = 0x1, - DISCOVERY_FAILED = 0x2, - INACTIVE = 0x3, - VALIDATION_UNDERWAY = 0x4, - RESERVED1 = 0x5, - RESERVED2 = 0x6, - RESERVED3 = 0x7, -}; - -/** Events specific to OneWaitress usage. */ -enum OneWaitressEvents { - STACK_STATUS_NETWORK_UP = 'STACK_STATUS_NETWORK_UP', - STACK_STATUS_NETWORK_DOWN = 'STACK_STATUS_NETWORK_DOWN', - STACK_STATUS_NETWORK_OPENED = 'STACK_STATUS_NETWORK_OPENED', - STACK_STATUS_NETWORK_CLOSED = 'STACK_STATUS_NETWORK_CLOSED', -}; - -enum NetworkInitAction { - /** Ain't that nice! */ - DONE, - /** Config mismatch, must leave network. */ - LEAVE, - /** Config mismatched, left network. Will evaluate forming from backup or config next. */ - LEFT, - /** Form the network using config. No backup, or backup mismatch. */ - FORM_CONFIG, - /** Re-form the network using full backed-up data. */ - FORM_BACKUP, -}; - -/** NOTE: Drivers can override `manufacturer`. Verify logic doesn't work in most cases anyway. */ -const autoDetectDefinitions = [ - /** NOTE: Manuf code "0x1321" for "Shenzhen Sonoff Technologies Co., Ltd." */ - {manufacturer: 'ITEAD', vendorId: '1a86', productId: '55d4'},// Sonoff ZBDongle-E - /** NOTE: Manuf code "0x134B" for "Nabu Casa, Inc." */ - {manufacturer: 'Nabu Casa', vendorId: '10c4', productId: 'ea60'},// Home Assistant SkyConnect -]; - -/** - * Config for EMBER_LOW_RAM_CONCENTRATOR type concentrator. - * - * Based on ZigbeeMinimalHost/zigpc - */ -const LOW_RAM_CONCENTRATOR_CONFIG: ConcentratorConfig = { - minTime: 5,// zigpc: 10 - maxTime: 60,// zigpc: 60 - routeErrorThreshold: 3,// zigpc: 3 - deliveryFailureThreshold: 1,// zigpc: 1, ZigbeeMinimalHost: 3 - mapHops: 0,// zigpc: 0 -}; -/** - * Config for EMBER_HIGH_RAM_CONCENTRATOR type concentrator. - * - * XXX: For now, same as low, until proper values can be determined. - */ -const HIGH_RAM_CONCENTRATOR_CONFIG: ConcentratorConfig = { - minTime: 5, - maxTime: 60, - routeErrorThreshold: 3, - deliveryFailureThreshold: 1, - mapHops: 0, -}; - -/** - * Application generated ZDO messages use sequence numbers 0-127, and the stack - * uses sequence numbers 128-255. This simplifies life by eliminating the need - * for coordination between the two entities, and allows both to send ZDO - * 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; -/** 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. */ -const BACKUP_OLDEST_SUPPORTED_EZSP_VERSION = 12; -/** - * 9sec is minimum recommended for `ezspBroadcastNextNetworkKey` to have propagated throughout network. - * NOTE: This is blocking the request queue, so we shouldn't go crazy high. - */ -const BROADCAST_NETWORK_KEY_SWITCH_WAIT_TIME = 15000; - -/** - * Stack configuration values for various supported stacks. - */ -const STACK_CONFIGS = { - "default": { - /** <1-250> (Default: 2) @see EzspConfigId.ADDRESS_TABLE_SIZE */ - ADDRESS_TABLE_SIZE: 16,// zigpc: 32, darkxst: 16 - /** <0-4> (Default: 2) @see EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE */ - TRUST_CENTER_ADDRESS_CACHE_SIZE: 2, - /** (Default: USE_TOKEN) @see EzspConfigId.TX_POWER_MODE */ - TX_POWER_MODE: EmberTXPowerMode.USE_TOKEN, - /** <-> (Default: 1) @see EzspConfigId.SUPPORTED_NETWORKS */ - SUPPORTED_NETWORKS: 1, - /** <-> (Default: ) @see EzspConfigId.STACK_PROFILE */ - STACK_PROFILE: STACK_PROFILE_ZIGBEE_PRO, - /** <-> (Default: ) @see EzspConfigId.SECURITY_LEVEL */ - SECURITY_LEVEL: SECURITY_LEVEL_Z3, - /** (Default: KEEP_ALIVE_SUPPORT_ALL) @see EzspValueId.END_DEVICE_KEEP_ALIVE_SUPPORT_MODE */ - END_DEVICE_KEEP_ALIVE_SUPPORT_MODE: EmberKeepAliveMode.KEEP_ALIVE_SUPPORT_ALL,// zigpc: KEEP_ALIVE_SUPPORT_ALL - /** <-> (Default: MAXIMUM_APS_PAYLOAD_LENGTH) @see EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE */ - MAXIMUM_INCOMING_TRANSFER_SIZE: MAXIMUM_APS_PAYLOAD_LENGTH, - /** <-> (Default: MAXIMUM_APS_PAYLOAD_LENGTH) @see EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE */ - MAXIMUM_OUTGOING_TRANSFER_SIZE: MAXIMUM_APS_PAYLOAD_LENGTH, - /** <-> (Default: 10000) @see EzspValueId.TRANSIENT_DEVICE_TIMEOUT */ - TRANSIENT_DEVICE_TIMEOUT: 10000, - /** <0-127> (Default: 2) @see EzspConfigId.BINDING_TABLE_SIZE */ - BINDING_TABLE_SIZE: 5,// zigpc: 2, Z3GatewayGPCombo: 5 - /** <0-127> (Default: 0) @see EzspConfigId.KEY_TABLE_SIZE */ - KEY_TABLE_SIZE: 0,// zigpc: 4 - /** <6-64> (Default: 6) @see EzspConfigId.MAX_END_DEVICE_CHILDREN */ - MAX_END_DEVICE_CHILDREN: 6,// zigpc: 6 - /** <1-255> (Default: 10) @see EzspConfigId.APS_UNICAST_MESSAGE_COUNT */ - APS_UNICAST_MESSAGE_COUNT: 20,// zigpc: 10, darkxst: 20 - /** <15-254> (Default: 15) @see EzspConfigId.BROADCAST_TABLE_SIZE */ - BROADCAST_TABLE_SIZE: 15,// zigpc: 15, Z3GatewayGPCombo: 35 - NOTE: Sonoff Dongle-E fails at 35 - /** [1, 16, 26] (Default: 16). @see EzspConfigId.NEIGHBOR_TABLE_SIZE */ - NEIGHBOR_TABLE_SIZE: 26,// zigpc: 16, darkxst: 26 - /** (Default: 8) @see EzspConfigId.END_DEVICE_POLL_TIMEOUT */ - END_DEVICE_POLL_TIMEOUT: 8,// zigpc: 8 - /** <0-65535> (Default: 300) @see EzspConfigId.TRANSIENT_KEY_TIMEOUT_S */ - TRANSIENT_KEY_TIMEOUT_S: 300,// zigpc: 65535 - /** <-> (Default: 16) @see EzspConfigId.RETRY_QUEUE_SIZE */ - RETRY_QUEUE_SIZE: 16, - /** <0-255> (Default: 0) @see EzspConfigId.SOURCE_ROUTE_TABLE_SIZE */ - SOURCE_ROUTE_TABLE_SIZE: 200,// Z3GatewayGPCombo: 100, darkxst: 200 - /** <1-250> (Default: 8) @see EzspConfigId.MULTICAST_TABLE_SIZE */ - MULTICAST_TABLE_SIZE: 16,// darkxst: 16 - }, - "zigbeed": { - ADDRESS_TABLE_SIZE: 128, - TRUST_CENTER_ADDRESS_CACHE_SIZE: 2, - TX_POWER_MODE: EmberTXPowerMode.USE_TOKEN, - SUPPORTED_NETWORKS: 1, - STACK_PROFILE: STACK_PROFILE_ZIGBEE_PRO, - SECURITY_LEVEL: SECURITY_LEVEL_Z3, - END_DEVICE_KEEP_ALIVE_SUPPORT_MODE: EmberKeepAliveMode.KEEP_ALIVE_SUPPORT_ALL, - MAXIMUM_INCOMING_TRANSFER_SIZE: MAXIMUM_APS_PAYLOAD_LENGTH, - MAXIMUM_OUTGOING_TRANSFER_SIZE: MAXIMUM_APS_PAYLOAD_LENGTH, - TRANSIENT_DEVICE_TIMEOUT: 10000, - BINDING_TABLE_SIZE: 128, - KEY_TABLE_SIZE: 0,// zigbeed 128 - MAX_END_DEVICE_CHILDREN: 64, - APS_UNICAST_MESSAGE_COUNT: 32, - BROADCAST_TABLE_SIZE: 15, - NEIGHBOR_TABLE_SIZE: 26, - END_DEVICE_POLL_TIMEOUT: 8, - TRANSIENT_KEY_TIMEOUT_S: 300, - RETRY_QUEUE_SIZE: 16, - SOURCE_ROUTE_TABLE_SIZE: 254, - MULTICAST_TABLE_SIZE: 128, - /* - ROUTE_TABLE_SIZE: 254, - DISCOVERY_TABLE_SIZE: 64, - PACKET_BUFFER_COUNT: 255, - CUSTOM_MAC_FILTER_TABLE_SIZE: 64, - MAC_FILTER_TABLE_SIZE: 32, - CHILD_TABLE_SIZE: 64, - PLUGIN_ZIGBEE_PRO_STACK_CHILD_TABLE_SIZE: 64, - APS_MESSAGE_COUNT: 64, - */ - }, -}; - -/** - * 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. */ -const DEFAULT_ZCL_REQUEST_TIMEOUT = 15000;//msec -/** Time for a network-related request to get a response (usually via event). */ -const DEFAULT_NETWORK_REQUEST_TIMEOUT = 10000;// nothing on the network to bother requests, should be much faster than this -/** Time between watchdog counters reading/clearing */ -const WATCHDOG_COUNTERS_FEED_INTERVAL = 3600000;// every hour... - -/** - * Relay calls between Z2M and EZSP-layer and handle any error that might occur via queue & waitress. - * - * Anything post `start` that requests anything from the EZSP layer must run through the request queue for proper execution flow. - */ -export class EmberAdapter extends Adapter { - /** Key in STACK_CONFIGS */ - public readonly stackConfig: 'default' | 'zigbeed'; - /** EMBER_LOW_RAM_CONCENTRATOR or EMBER_HIGH_RAM_CONCENTRATOR. */ - private concentratorType: number; - - private readonly ezsp: Ezsp; - private version: {ezsp: number, revision: string} & EmberVersion; - - private requestQueue: EmberRequestQueue; - private oneWaitress: EmberOneWaitress; - /** Periodically retrieve counters then clear them. */ - private watchdogCountersHandle: NodeJS.Timeout; - - /** Hold ZDO request in process. */ - private zdoRequestBuffalo: EzspBuffalo; - /** Sequence number used for ZDO requests. static uint8_t */ - private zdoRequestSequence: number; - /** Default radius used for broadcast ZDO requests. uint8_t */ - private zdoRequestRadius: number; - - private interpanLock: boolean; - - /** - * Cached network params to avoid NCP calls. Prevents frequent EZSP transactions. - * NOTE: Do not use directly, use getter functions for it that check if valid or need retrieval from NCP. - */ - private networkCache: NetworkCache; - - private defaultApsOptions: EmberApsOption; - - /** - * Mirrors the NCP multicast table. null === not in use. - * Index 0 is Green Power and must always remain there. - */ - private multicastTable: EmberMulticastTableEntry[]; - - constructor(networkOptions: TsType.NetworkOptions, serialPortOptions: TsType.SerialPortOptions, backupPath: string, - adapterOptions: TsType.AdapterOptions, logger?: LoggerStub) { - super(networkOptions, serialPortOptions, backupPath, adapterOptions, logger); - - // TODO config, should be fine like this for now? - this.stackConfig = SocketPortUtils.isTcpPath(serialPortOptions.path) ? 'zigbeed' : 'default'; - // TODO config - this.concentratorType = EMBER_HIGH_RAM_CONCENTRATOR; - - // TODO: config dispatch interval, tested at 100, 80, 60 - this.requestQueue = new EmberRequestQueue(60); - this.oneWaitress = new EmberOneWaitress(); - this.zdoRequestBuffalo = new EzspBuffalo(Buffer.alloc(EZSP_MAX_FRAME_LENGTH)); - - // TODO: config tick interval, tested at 500, 300, 100, 60, 30, all work fine and only really noticeable with interviews - this.ezsp = new Ezsp(60, serialPortOptions); - - this.ezsp.on(EzspEvents.STACK_STATUS, this.onStackStatus.bind(this)); - - this.ezsp.on(EzspEvents.MESSAGE_SENT_DELIVERY_FAILED, this.onMessageSentDeliveryFailed.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.TRUST_CENTER_JOIN, this.onTrustCenterJoin.bind(this)); - } - - /** - * Emitted from @see Ezsp.ezspStackStatusHandler - * @param status - */ - private async onStackStatus(status: EmberStatus): Promise { - // to be extra careful, should clear network cache upon receiving this. - this.clearNetworkCache(); - - switch (status) { - case EmberStatus.NETWORK_UP: { - this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_UP); - console.log(`[STACK STATUS] Network up.`); - break; - } - case EmberStatus.NETWORK_DOWN: { - this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_DOWN); - console.log(`[STACK STATUS] Network down.`); - break; - } - case EmberStatus.NETWORK_OPENED: { - this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_OPENED); - this.requestQueue.enqueue( - async (): Promise => { - const setJPstatus = (await this.emberSetJoinPolicy(EmberJoinDecision.USE_PRECONFIGURED_KEY)); - - if (setJPstatus !== EzspStatus.SUCCESS) { - console.error(`[ZDO] Failed set join policy for with status=${EzspStatus[setJPstatus]}.`); - return EmberStatus.ERR_FATAL; - } - - return EmberStatus.SUCCESS; - }, - console.error,// no reject, just log error if any - true,// prioritize just to avoid delays if queue is busy - ); - console.log(`[STACK STATUS] Network opened.`); - break; - } - case EmberStatus.NETWORK_CLOSED: { - this.oneWaitress.resolveEvent(OneWaitressEvents.STACK_STATUS_NETWORK_CLOSED); - console.log(`[STACK STATUS] Network closed.`); - break; - } - default: { - debug(`[STACK STATUS] ${EmberStatus[status]}.`); - break; - } - } - } - - /** - * Emitted from @see Ezsp.ezspMessageSentHandler - * WARNING: Cannot rely on `ezspMessageSentHandler` > `ezspIncomingMessageHandler` order, some devices mix it up! - * - * @param type - * @param indexOrDestination - * @param apsFrame - * @param messageTag - */ - private async onMessageSentDeliveryFailed(type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number) - : Promise { - switch (type) { - case EmberOutgoingMessageType.BROADCAST: - case EmberOutgoingMessageType.BROADCAST_WITH_ALIAS: - case EmberOutgoingMessageType.MULTICAST: - case EmberOutgoingMessageType.MULTICAST_WITH_ALIAS: { - // BC/MC not checking for message sent, avoid unnecessary waitress lookups - console.error(`Delivery of ${EmberOutgoingMessageType[type]} failed for "${indexOrDestination}" ` - + `[apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`); - break; - } - default: { - // reject any waitress early (don't wait for timeout if we know we're gonna get there eventually) - this.oneWaitress.deliveryFailedFor(indexOrDestination, apsFrame); - break; - } - } - } - - /** - * Emitted from @see Ezsp.ezspIncomingMessageHandler - * - * @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. - */ - private async onZDOResponse(status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: unknown) - : Promise { - this.oneWaitress.resolveZDO(status, sender, apsFrame, payload); - } - - /** - * Emitted from @see Ezsp.ezspIncomingMessageHandler - * - * @param sender - * @param nodeId - * @param eui64 - * @param macCapFlags - */ - private async onEndDeviceAnnounce(sender: EmberNodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload): Promise { - // reduced function device - // if ((payload.capabilities.deviceType === 0)) { - - // } - - this.emit(Events.deviceAnnounce, {networkAddress: payload.nodeId, ieeeAddr: payload.eui64} as DeviceAnnouncePayload); - } - - /** - * Emitted from @see Ezsp.ezspIncomingMessageHandler - * - * @param type - * @param apsFrame - * @param lastHopLqi - * @param sender - * @param messageContents - */ - private async onIncomingMessage(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: EmberNodeId, - messageContents: Buffer): Promise { - try { - const payload: ZclDataPayload = { - address: sender, - frame: ZclFrame.fromBuffer(apsFrame.clusterId, messageContents), - endpoint: apsFrame.sourceEndpoint, - linkquality: lastHopLqi, - groupID: apsFrame.groupId, - wasBroadcast: ((type === EmberIncomingMessageType.BROADCAST) || (type === EmberIncomingMessageType.BROADCAST_LOOPBACK)), - destinationEndpoint: apsFrame.destinationEndpoint, - }; - - this.oneWaitress.resolveZCL(payload); - this.emit(Events.zclData, payload); - } catch (error) { - const payload: RawDataPayload = { - clusterID: apsFrame.clusterId, - address: sender, - data: messageContents, - endpoint: apsFrame.sourceEndpoint, - linkquality: lastHopLqi, - groupID: apsFrame.groupId, - wasBroadcast: ((type === EmberIncomingMessageType.BROADCAST) || (type === EmberIncomingMessageType.BROADCAST_LOOPBACK)), - destinationEndpoint: apsFrame.destinationEndpoint, - }; - - this.emit(Events.rawData, payload); - } - } - - /** - * Emitted from @see Ezsp.ezspMacFilterMatchMessageHandler when the message is a valid InterPAN touchlink message. - * - * @param sourcePanId - * @param sourceAddress - * @param groupId - * @param lastHopLqi - * @param messageContents - */ - private async onTouchlinkMessage(sourcePanId: EmberPanId, sourceAddress: EmberEUI64, groupId: number | null, lastHopLqi: number, - messageContents: Buffer): Promise { - const payload: ZclDataPayload = { - frame: ZclFrame.fromBuffer(Cluster.touchlink.ID, messageContents), - address: sourceAddress, - endpoint: 1,// arbitrary since not sent over-the-air - linkquality: lastHopLqi, - groupID: groupId, - wasBroadcast: true,// XXX: since always sent broadcast atm... - destinationEndpoint: FIXED_ENDPOINTS[0].endpoint, - }; - - this.oneWaitress.resolveZCL(payload); - this.emit(Events.zclData, payload); - } - - /** - * Emitted from @see Ezsp.ezspGpepIncomingMessageHandler - * - * @param sender uint32_t or EmberEUI64 depending on `EmberGpApplicationId`. See emitter - * @param gpdCommandId - * @param gpdLink - * @param sequenceNumber - * @param deviceId - * @param options - * @param key - * @param counter - */ - private async onGreenpowerMessage(sender: number | EmberEUI64, gpdCommandId: number, gpdLink: number, sequenceNumber: number, deviceId?: number, - options?: number, key?: EmberKeyData, counter?: number): Promise { - // TODO: all this stuff needs triple-checking, also with upstream (not really multi-adapter at first glance?) - // more params avail in EZSP handler - switch (gpdCommandId) { - case 0xE0: { - if (!key) { - return; - } - - // commissioning notification - const gpdMessage = { - // gppNwkAddr: ?,// XXX - commandID: gpdCommandId, - commandFrame: {options: options, securityKey: key.contents, deviceID: deviceId, outgoingCounter: counter}, - // XXX: Z2M seems to want only sourceId, but it isn't always present..? @see ezspGpepIncomingMessageHandler - srcID: sender, - }; - const zclFrame = ZclFrame.create(FrameType.SPECIFIC, Direction.CLIENT_TO_SERVER, true, null, sequenceNumber, 'commissioningNotification', - Cluster.greenPower.ID, gpdMessage); - const payload: ZclDataPayload = { - frame: zclFrame, - address: sender, - endpoint: GP_ENDPOINT, - linkquality: gpdLink, - groupID: null, - wasBroadcast: true, - destinationEndpoint: GP_ENDPOINT, - }; - - this.emit(Events.zclData, payload); - } - default:{// XXX: all the rest in one basket? - const gpdMessage = {commandID: gpdCommandId, srcID: sender};// same as above about `srcID` - const zclFrame = ZclFrame.create(FrameType.SPECIFIC, Direction.CLIENT_TO_SERVER, true, null, sequenceNumber, 'notification', - Cluster.greenPower.ID, gpdMessage); - - const payload: ZclDataPayload = { - frame: zclFrame, - address: sender, - endpoint: GP_ENDPOINT, - linkquality: gpdLink, - groupID: null, - wasBroadcast: true, - destinationEndpoint: GP_ENDPOINT, - }; - - this.emit(Events.zclData, payload); - } - } - } - - /** - * Emitted from @see Ezsp.ezspTrustCenterJoinHandler - * - * @param newNodeId - * @param newNodeEui64 - * @param status - * @param policyDecision - * @param parentOfNewNodeId - */ - private async onTrustCenterJoin(newNodeId: EmberNodeId, newNodeEui64: EmberEUI64, status: EmberDeviceUpdate, - policyDecision: EmberJoinDecision, parentOfNewNodeId: EmberNodeId): Promise { - if (status === EmberDeviceUpdate.DEVICE_LEFT) { - // NOTE: `policyDecision` here is NO_ACTION and `parentOfNewNodeId` is 65535 - const payload: DeviceLeavePayload = { - networkAddress: newNodeId, - ieeeAddr: newNodeEui64, - }; - - this.emit(Events.deviceLeave, payload); - } else { - if (policyDecision !== EmberJoinDecision.DENY_JOIN) { - const payload: DeviceJoinedPayload = { - networkAddress: newNodeId, - ieeeAddr: newNodeEui64, - }; - - this.emit(Events.deviceJoined, payload); - } else { - console.log(`[TRUST CENTER] Device ${newNodeId}:${newNodeEui64} was denied joining via ${parentOfNewNodeId}.`); - } - } - } - - private async watchdogCounters(): Promise { - this.requestQueue.enqueue( - async (): Promise => { - // listed as per EmberCounterType - const counters = (await this.ezsp.ezspReadAndClearCounters()); - - let countersLogString = "[NCP COUNTERS] "; - - for (let i = 0; i < EmberCounterType.COUNT; i++) { - countersLogString += `${EmberCounterType[i]}: ${counters[i]} | `; - } - - console.log(countersLogString); - - return EmberStatus.SUCCESS; - }, - console.error,// no reject, just log error if any - ); - } - - private initVariables(): void { - this.ezsp.removeAllListeners(EzspEvents.ncpNeedsResetAndInit); - - clearInterval(this.watchdogCountersHandle); - - this.zdoRequestBuffalo.setPosition(0); - this.zdoRequestSequence = 0;// start at 1 - this.zdoRequestRadius = 255; - - this.interpanLock = false; - - this.networkCache = initNetworkCache(); - - this.defaultApsOptions = (EmberApsOption.RETRY | EmberApsOption.ENABLE_ROUTE_DISCOVERY | EmberApsOption.ENABLE_ADDRESS_DISCOVERY); - - // always at least length==1 because of allowed MULTICAST_TABLE_SIZE range - this.multicastTable = new Array(STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE).fill(null); - - this.ezsp.once(EzspEvents.ncpNeedsResetAndInit, this.onNcpNeedsResetAndInit.bind(this)); - } - - /** - * Proceed to execute the long list of commands required to setup comms between Host<>NCP. - * This is called by start and on internal reset. - */ - private async initEzsp(): Promise { - let result: TsType.StartResult = "resumed"; - - await this.onNCPPreReset(); - - try { - // NOTE: something deep in this call can throw too - const result = (await this.ezsp.start()); - - if (result !== EzspStatus.SUCCESS) { - throw new Error(`Failed to start EZSP layer with status=${EzspStatus[result]}.`); - } - } catch (err) { - throw err; - } - - // call before any other command, else fails - await this.emberVersion(); - - await this.initNCPPreConfiguration(); - await this.initNCPAddressTable(); - await this.initNCPConfiguration(); - - // WARNING: From here on EZSP commands that affect memory allocation on the NCP should no longer be called (like resizing tables) - - await this.onNCPPostReset(); - await this.registerFixedEndpoints(); - this.clearNetworkCache(); - - result = (await this.initTrustCenter()); - - // 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()); - - if (status !== EmberStatus.SUCCESS) { - throw new Error(`Failed to get network parameters with status=${EmberStatus[status]}.`); - } - - this.networkCache.parameters = parameters; - this.networkCache.status = (await this.ezsp.ezspNetworkState()); - this.networkCache.eui64 = (await this.ezsp.ezspGetEui64()); - - debug(`[INIT] Network Ready! ${JSON.stringify(this.networkCache)}`); - - 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, STACK_CONFIGS[this.stackConfig].ADDRESS_TABLE_SIZE); - await this.emberSetEzspConfigValue( - EzspConfigId.TRUST_CENTER_ADDRESS_CACHE_SIZE, - STACK_CONFIGS[this.stackConfig].TRUST_CENTER_ADDRESS_CACHE_SIZE - ); - - if (STACK_CONFIGS[this.stackConfig].STACK_PROFILE === STACK_PROFILE_ZIGBEE_PRO) { - // 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, STACK_CONFIGS[this.stackConfig].TX_POWER_MODE); - await this.emberSetEzspConfigValue(EzspConfigId.SUPPORTED_NETWORKS, STACK_CONFIGS[this.stackConfig].SUPPORTED_NETWORKS); - - await this.emberSetEzspValue( - EzspValueId.END_DEVICE_KEEP_ALIVE_SUPPORT_MODE, - 1, - [STACK_CONFIGS[this.stackConfig].END_DEVICE_KEEP_ALIVE_SUPPORT_MODE] - ); - - // 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 and message contents in ezspMessageSentHandler() - await this.emberSetEzspPolicy( - EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, - EzspDecisionId.MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK - ); - - await this.emberSetEzspValue( - EzspValueId.MAXIMUM_INCOMING_TRANSFER_SIZE, - 2, - lowHighBytes(STACK_CONFIGS[this.stackConfig].MAXIMUM_INCOMING_TRANSFER_SIZE) - ); - await this.emberSetEzspValue( - EzspValueId.MAXIMUM_OUTGOING_TRANSFER_SIZE, - 2, - lowHighBytes(STACK_CONFIGS[this.stackConfig].MAXIMUM_OUTGOING_TRANSFER_SIZE) - ); - await this.emberSetEzspValue( - EzspValueId.TRANSIENT_DEVICE_TIMEOUT, - 2, - lowHighBytes(STACK_CONFIGS[this.stackConfig].TRANSIENT_DEVICE_TIMEOUT) - ); - - // Set the manufacturing code. This is defined by ZigBee document 053874r10 - // Ember's ID is 0x1002 and is the default, but this can be overridden in App Builder. - await this.ezsp.ezspSetManufacturerCode(MANUFACTURER_CODE); - - // network security init - await this.emberSetEzspConfigValue(EzspConfigId.STACK_PROFILE, STACK_CONFIGS[this.stackConfig].STACK_PROFILE); - await this.emberSetEzspConfigValue(EzspConfigId.SECURITY_LEVEL, STACK_CONFIGS[this.stackConfig].SECURITY_LEVEL); - } - - /** - * NCP Address table init. - * @returns - */ - private async initNCPAddressTable(): Promise { - const desiredTableSize = STACK_CONFIGS[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 !== EzspStatus.SUCCESS) || (addressTableSize > desiredTableSize)) { - throw new Error( - `[INIT] NCP (${addressTableSize}) disagrees with Host (min ${desiredTableSize}) on table size. status=${EzspStatus[status]}` - ); - } - } - - /** - * NCP configuration init - */ - private async initNCPConfiguration(): Promise { - await this.emberSetEzspConfigValue(EzspConfigId.BINDING_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].BINDING_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.KEY_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.MAX_END_DEVICE_CHILDREN, STACK_CONFIGS[this.stackConfig].MAX_END_DEVICE_CHILDREN); - await this.emberSetEzspConfigValue(EzspConfigId.APS_UNICAST_MESSAGE_COUNT, STACK_CONFIGS[this.stackConfig].APS_UNICAST_MESSAGE_COUNT); - await this.emberSetEzspConfigValue(EzspConfigId.BROADCAST_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].BROADCAST_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.NEIGHBOR_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].NEIGHBOR_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.END_DEVICE_POLL_TIMEOUT, STACK_CONFIGS[this.stackConfig].END_DEVICE_POLL_TIMEOUT); - await this.emberSetEzspConfigValue(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, STACK_CONFIGS[this.stackConfig].TRANSIENT_KEY_TIMEOUT_S); - await this.emberSetEzspConfigValue(EzspConfigId.RETRY_QUEUE_SIZE, STACK_CONFIGS[this.stackConfig].RETRY_QUEUE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.SOURCE_ROUTE_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].SOURCE_ROUTE_TABLE_SIZE); - await this.emberSetEzspConfigValue(EzspConfigId.MULTICAST_TABLE_SIZE, STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE); - } - - /** - * NCP concentrator init. Also enables source route discovery mode with RESCHEDULE. - * - * From AN1233: - * To function correctly in a Zigbee PRO network, a trust center also requires that: - * - * 1. The trust center application must act as a concentrator (either high or low RAM). - * 2. The trust center application must have support for source routing. - * It must record the source routes and properly handle requests by the stack for a particular source route. - * 3. The trust center application must use an address cache for security, in order to maintain a mapping of IEEE address to short ID. - * - * Failure to satisfy all of the above requirements may result in failures when joining/rejoining devices to the network across multiple hops - * (through a target node that is neither the trust center nor one of its neighboring routers.) - */ - private async initNCPConcentrator(): Promise { - const config = (this.concentratorType === EMBER_HIGH_RAM_CONCENTRATOR) ? HIGH_RAM_CONCENTRATOR_CONFIG : LOW_RAM_CONCENTRATOR_CONFIG; - const status = (await this.ezsp.ezspSetConcentrator( - true, - this.concentratorType, - config.minTime, - config.maxTime, - config.routeErrorThreshold, - config.deliveryFailureThreshold, - config.mapHops, - )); - - if (status !== EmberStatus.SUCCESS) { - throw new Error(`[CONCENTRATOR] Failed to set concentrator with status=${status}.`); - } - - const remainTilMTORR = (await this.ezsp.ezspSetSourceRouteDiscoveryMode(EmberSourceRouteDiscoveryMode.RESCHEDULE)); - - console.log(`[CONCENTRATOR] Started source route discovery. ${remainTilMTORR}ms until next broadcast.`); - } - - /** - * Register fixed endpoints and set any related multicast entries that need to be. - */ - private async registerFixedEndpoints(): Promise { - for (const ep of FIXED_ENDPOINTS) { - if (ep.networkIndex !== 0x00) { - debug(`Multi-network not currently supported. Skipping endpoint ${JSON.stringify(ep)}.`); - continue; - } - - const [epStatus,] = (await this.ezsp.ezspGetEndpointFlags(ep.endpoint)); - - // endpoint not already registered - if (epStatus !== EzspStatus.SUCCESS) { - // 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( - ep.endpoint, - ep.profileId, - ep.deviceId, - ep.deviceVersion, - ep.inClusterList, - ep.outClusterList, - )); - - if (status === EzspStatus.SUCCESS) { - debug(`Registered endpoint "${ep.endpoint}" with status=${EzspStatus[status]}.`); - } else { - throw new Error(`Failed to register endpoint "${ep.endpoint}" with status=${EzspStatus[status]}.`); - } - } else { - debug(`Endpoint "${ep.endpoint}" already registered.`); - } - - if (ep.endpoint === GP_ENDPOINT) { - const gpMulticastEntry: EmberMulticastTableEntry = { - multicastId: this.greenPowerGroup, - endpoint: ep.endpoint, - networkIndex: ep.networkIndex, - }; - - const status = (await this.ezsp.ezspSetMulticastTableEntry(0, gpMulticastEntry)); - - if (status !== EmberStatus.SUCCESS) { - throw new Error(`Failed to register group "Green Power" in multicast table with status=${EmberStatus[status]}.`); - } - - // NOTE: ensure GP is always added first in the table - this.multicastTable[0] = gpMulticastEntry; - debug(`Registered multicast table entry: ${JSON.stringify(gpMulticastEntry)}.`); - } - } - } - - /** - * - * @returns True if the network needed to be formed. - */ - private async initTrustCenter(): Promise { - // init TC policies - { - let status = (await this.emberSetEzspPolicy( - EzspPolicyId.TC_KEY_REQUEST_POLICY, - EzspDecisionId.ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY, - )); - - if (status !== EzspStatus.SUCCESS) { - 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]}.`); - } - - status = (await this.emberSetEzspPolicy( - EzspPolicyId.APP_KEY_REQUEST_POLICY, - STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE ? EzspDecisionId.ALLOW_APP_KEY_REQUESTS : EzspDecisionId.DENY_APP_KEY_REQUESTS, - )); - - if (status !== EzspStatus.SUCCESS) { - throw new Error(`[INIT TC] Failed to set EzspPolicyId APP_KEY_REQUEST_POLICY to DENY_APP_KEY_REQUESTS ` - + `with status=${EzspStatus[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]}.`); - } - } - - const configNetworkKey = Buffer.from(this.networkOptions.networkKey); - const networkInitStruct: EmberNetworkInitStruct = { - bitmask: (EmberNetworkInitBitmask.PARENT_INFO_IN_TOKEN | EmberNetworkInitBitmask.END_DEVICE_REJOIN_ON_REBOOT) - }; - const initStatus = (await this.ezsp.ezspNetworkInit(networkInitStruct)); - - debug(`[INIT TC] Network init status=${EmberStatus[initStatus]}.`); - - if ((initStatus !== EmberStatus.SUCCESS) && (initStatus !== EmberStatus.NOT_JOINED)) { - throw new Error(`[INIT TC] Failed network init request with status=${EmberStatus[initStatus]}.`); - } - - let action: NetworkInitAction = NetworkInitAction.DONE; - - if (initStatus === EmberStatus.SUCCESS) { - // network - await this.oneWaitress.startWaitingForEvent( - {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_UP}, - DEFAULT_NETWORK_REQUEST_TIMEOUT, - '[INIT TC] Network init', - ); - - const [npStatus, nodeType, netParams] = (await this.ezsp.ezspGetNetworkParameters()); - - debug(`[INIT TC] Current network config=${JSON.stringify(this.networkOptions)}`); - debug(`[INIT TC] Current NCP network: nodeType=${EmberNodeType[nodeType]} params=${JSON.stringify(netParams)}`); - - if ((npStatus === EmberStatus.SUCCESS) && (nodeType === EmberNodeType.COORDINATOR) && (this.networkOptions.panID === netParams.panId) - && (equals(this.networkOptions.extendedPanID, netParams.extendedPanId)) - && (this.networkOptions.channelList.includes(netParams.radioChannel))) { - // 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)); - - if (nkStatus !== SLStatus.OK) { - throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); - } - - debug(`[INIT TC] Current NCP network: networkKey=${networkKey.contents.toString('hex')}`); - - // config doesn't match adapter anymore - if (!networkKey.contents.equals(configNetworkKey)) { - action = NetworkInitAction.LEAVE; - } - } else { - // config doesn't match adapter - action = NetworkInitAction.LEAVE; - } - - if (action === NetworkInitAction.LEAVE) { - console.log(`[INIT TC] NCP network does not match config. Leaving network...`); - const leaveStatus = (await this.ezsp.ezspLeaveNetwork()); - - if (leaveStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT TC] Failed leave network request with status=${EmberStatus[leaveStatus]}.`); - } - - await this.oneWaitress.startWaitingForEvent( - {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_DOWN}, - DEFAULT_NETWORK_REQUEST_TIMEOUT, - '[INIT TC] Leave network', - ); - - await Wait(200);// settle down - - action = NetworkInitAction.LEFT; - } - } - - const backup: Backup = (await this.getStoredBackup()); - - if ((initStatus === EmberStatus.NOT_JOINED) || (action === NetworkInitAction.LEFT)) { - // no network - if (backup != null) { - if ((this.networkOptions.panID === backup.networkOptions.panId) - && (Buffer.from(this.networkOptions.extendedPanID).equals(backup.networkOptions.extendedPanId)) - && (this.networkOptions.channelList.includes(backup.logicalChannel)) - && (configNetworkKey.equals(backup.networkOptions.networkKey))) { - // config matches backup - action = NetworkInitAction.FORM_BACKUP; - } else { - // config doesn't match backup - console.log(`[INIT TC] Config does not match backup.`); - action = NetworkInitAction.FORM_CONFIG; - } - } else { - // no backup - console.log(`[INIT TC] No valid backup found.`); - action = NetworkInitAction.FORM_CONFIG; - } - } else { - action = NetworkInitAction.DONE;// just to be clear - } - - //---- from here on, we assume everything is in place for whatever decision was taken above - - let result: TsType.StartResult = 'resumed'; - - switch (action) { - case NetworkInitAction.FORM_BACKUP: { - console.log(`[INIT TC] Forming from backup.`); - 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, - key: {contents: device.linkKey.key}, - outgoingFrameCounter: device.linkKey.txCounter, - incomingFrameCounter: device.linkKey.rxCounter, - }; - return key; - }); - - // before forming - await this.importLinkKeys(keyList); - - await this.formNetwork( - true,/*from backup*/ - backup.networkOptions.networkKey, - backup.networkKeyInfo.sequenceNumber, - backup.networkOptions.panId, - Array.from(backup.networkOptions.extendedPanId), - backup.logicalChannel, - backup.ezsp.hashed_tclk, - ); - - result = 'restored'; - break; - } - case NetworkInitAction.FORM_CONFIG: { - console.log(`[INIT TC] Forming from config.`); - await this.formNetwork( - false,/*from config*/ - configNetworkKey, - 0, - this.networkOptions.panID, - this.networkOptions.extendedPanID, - this.networkOptions.channelList[0], - randomBytes(EMBER_ENCRYPTION_KEY_SIZE),// rnd TC link key - ); - - result = 'reset'; - break; - } - case NetworkInitAction.DONE: { - console.log(`[INIT TC] NCP network matches config.`); - break; - } - default: { - throw new Error(`[INIT TC] Invalid action "${NetworkInitAction[action]}" for final stage.`); - } - } - - // can't let frame counter wrap to zero (uint32_t), will force a broadcast after init if getting too close - if (backup != null && (backup.networkKeyInfo.frameCounter > 0xFEEEEEEE)) { - // XXX: while this remains a pretty low occurrence in most (small) networks, - // currently Z2M won't support the key update because of one-way config... - // need to investigate handling this properly - - // console.warn(`[INIT TC] Network key frame counter is reaching its limit. Scheduling broadcast to update network key. ` - // + `This may result in some devices (especially battery-powered) temporarily losing connection.`); - // // 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 => { - // await this.broadcastNetworkKeyUpdate(); - - // return EmberStatus.SUCCESS; - // }, console.error, true);// no reject just log error if any, will retry next start, & prioritize so we know it'll run when expected - // }, 300000); - console.warn(`[INIT TC] Network key frame counter is reaching its limit. A new network key will have to be instaured soon.`); - } - - return result; - } - - /** - * Form a network using given parameters. - */ - private async formNetwork(fromBackup: boolean, networkKey: Buffer, networkKeySequenceNumber: number, panId: EmberPanId, - extendedPanId: EmberExtendedPanId, radioChannel: number, tcLinkKey: Buffer): Promise { - const state: EmberInitialSecurityState = { - bitmask: ( - EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY - | EmberInitialSecurityBitmask.HAVE_NETWORK_KEY | EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY - | EmberInitialSecurityBitmask.REQUIRE_ENCRYPTED_KEY - ), - preconfiguredKey: {contents: tcLinkKey}, - networkKey: {contents: networkKey}, - networkKeySequenceNumber: networkKeySequenceNumber, - preconfiguredTrustCenterEui64: BLANK_EUI64, - }; - - if (fromBackup) { - state.bitmask |= EmberInitialSecurityBitmask.NO_FRAME_COUNTER_RESET; - } - - let emberStatus = (await this.ezsp.ezspSetInitialSecurityState(state)); - - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed to set initial security state with status=${EmberStatus[emberStatus]}.`); - } - - const extended: EmberExtendedSecurityBitmask = ( - EmberExtendedSecurityBitmask.JOINER_GLOBAL_LINK_KEY | EmberExtendedSecurityBitmask.NWK_LEAVE_REQUEST_NOT_ALLOWED - ); - const extSecStatus = (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 (!fromBackup && STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE) { - emberStatus = await this.ezsp.ezspClearKeyTable(); - - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed to clear key table with status=${EmberStatus[emberStatus]}.`); - } - } - - const netParams: EmberNetworkParameters = { - panId, - extendedPanId, - radioTxPower: 5, - radioChannel, - joinMethod: EmberJoinMethod.MAC_ASSOCIATION, - nwkManagerId: ZIGBEE_COORDINATOR_ADDRESS, - nwkUpdateId: 0, - channels: EMBER_ALL_802_15_4_CHANNELS_MASK, - }; - - console.log(`[INIT FORM] Forming new network with: ${JSON.stringify(netParams)}`); - - emberStatus = (await this.ezsp.ezspFormNetwork(netParams)); - - if (emberStatus !== EmberStatus.SUCCESS) { - throw new Error(`[INIT FORM] Failed form network request with status=${EmberStatus[emberStatus]}.`); - } - - await this.oneWaitress.startWaitingForEvent( - {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_UP}, - DEFAULT_NETWORK_REQUEST_TIMEOUT, - '[INIT FORM] Form network', - ); - - const stStatus = await this.ezsp.ezspStartWritingStackTokens(); - - debug(`[INIT FORM] Start writing stack tokens status=${EzspStatus[stStatus]}.`); - - console.log(`[INIT FORM] New network formed!`); - } - - /** - * Loads currently stored backup and returns it in internal backup model. - */ - public async getStoredBackup(): Promise { - try { - await fs.access(this.backupPath); - } catch (error) { - return null; - } - - let data: UnifiedBackupStorage; - - try { - data = JSON.parse((await fs.readFile(this.backupPath)).toString()); - } catch (error) { - throw new Error(`[BACKUP] Coordinator backup is corrupted.`); - } - - if (data.metadata?.format === "zigpy/open-coordinator-backup" && data.metadata?.version) { - if (data.metadata?.version !== 1) { - throw new Error(`[BACKUP] Unsupported open coordinator backup version (version=${data.metadata?.version}).`); - } - - if (!data.stack_specific?.ezsp || !data.metadata.internal.ezspVersion) { - throw new Error(`[BACKUP] Specified backup is not EZSP.`); - } - - if (data.metadata.internal.ezspVersion < BACKUP_OLDEST_SUPPORTED_EZSP_VERSION) { - throw new Error(`[BACKUP] Specified backup is not a supported EZSP version (min: ${BACKUP_OLDEST_SUPPORTED_EZSP_VERSION}).`); - } - - return BackupUtils.fromUnifiedBackup(data); - } else { - throw new Error(`[BACKUP] Unknown backup format.`); - } - } - - /** - * Export link keys for backup. - * - * @return List of keys data with AES hashed keys - */ - 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]}.`); - } - - let deviceEui64: EmberEUI64; - 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)); - debug(`[BACKUP] Export link key at index ${i}, status=${SLStatus[status]}.`); - - // only include key if we could retrieve one at index and hash it properly - if (status === SLStatus.OK) { - // Rather than give the real link key, the backup contains a hashed version of the key. - // This is done to prevent a compromise of the backup data from compromising the current link keys. - // This is per the Smart Energy spec. - const [hashStatus, hashedKey] = (await this.emberAesHashSimple(plaintextKey.contents)); - - if (hashStatus === EmberStatus.SUCCESS) { - keyList.push({ - deviceEui64, - key: {contents: hashedKey}, - outgoingFrameCounter: apsKeyMeta.outgoingFrameCounter, - incomingFrameCounter: apsKeyMeta.incomingFrameCounter, - }); - } else { - // this should never happen? - console.error(`[BACKUP] Failed to hash link key at index ${i} with status=${EmberStatus[hashStatus]}. Omitting from backup.`); - } - } - } - - console.log(`[BACKUP] Retrieved ${keyList.length} link keys.`); - - return keyList; - } - - /** - * Import link keys from backup. - * - * @param backupData - */ - public async importLinkKeys(backupData: LinkKeyBackupData[]): Promise { - if (!backupData?.length) { - return; - } - - 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 (backupData.length > keyTableSize) { - throw new Error(`[BACKUP] Current key table of ${keyTableSize} is too small to import backup of ${backupData.length}!`); - } - - const networkStatus = (await this.emberNetworkState()); - - if (networkStatus !== EmberNetworkStatus.NO_NETWORK) { - throw new Error(`[BACKUP] Cannot import TC data while network is up, networkStatus=${EmberNetworkStatus[networkStatus]}.`); - } - - let status: EmberStatus; - - 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); - } - - if (status !== EmberStatus.SUCCESS) { - throw new Error(`[BACKUP] Failed to ${((i >= backupData.length) ? "erase" : "set")} key table entry at index ${i} ` - + `with status=${EmberStatus[status]}`); - } - } - - debug(`[BACKUP] Imported ${backupData.length} keys.`); - } - - /** - * Routine to update the network key and broadcast the update to the network after a set time. - * NOTE: This should run at a large interval, but before the uint32_t of the frame counter is able to reach all Fs (can't wrap to 0). - * This may disrupt sleepy end devices that miss the update, but they should be able to TC rejoin (in most cases...). - * On the other hand, the more often this runs, the more secure the network is... - */ - private async broadcastNetworkKeyUpdate(): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - console.warn(`[TRUST CENTER] Performing a network key update. This might take a while and disrupt normal operation.`); - - // 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) { - console.error(`[TRUST CENTER] Failed to broadcast next network key with status=${EmberStatus[status]}.`); - return status; - } - - // XXX: this will block other requests for a while, but should ensure the key propagates without interference? - // could also stop dispatching entirely and do this outside the queue if necessary/better - await Wait(BROADCAST_NETWORK_KEY_SWITCH_WAIT_TIME); - - status = (await this.ezsp.ezspBroadcastNetworkKeySwitch()); - - if (status !== EmberStatus.SUCCESS) { - // XXX: Not sure how likely this is, but this is bad, probably should hard fail? - console.error(`[TRUST CENTER] Failed to broadcast network key switch with status=${EmberStatus[status]}.`); - return status; - } - - resolve(); - return status; - }, - reject, - ); - }); - } - - /** - * Received when EZSP layer alerts of a problem that needs the NCP to be reset. - * @param status - */ - private async onNcpNeedsResetAndInit(status: EzspStatus): Promise { - console.error(`!!! NCP FATAL ERROR reason=${EzspStatus[status]}. ATTEMPTING RESET... !!!`); - - try { - await this.stop(); - await Wait(500);// just because - await this.start(); - } catch (err) { - console.error(`Failed to reset and init NCP. ${err}`); - this.emit(Events.disconnected); - } - } - - /** - * Called right before a NCP reset. - */ - private async onNCPPreReset(): Promise { - this.requestQueue.stopDispatching(); - } - - /** - * Called right after a NCP reset, right before the creation of endpoints. - */ - private async onNCPPostReset(): Promise { - this.requestQueue.startDispatching(); - - this.watchdogCountersHandle = setInterval(this.watchdogCounters.bind(this), WATCHDOG_COUNTERS_FEED_INTERVAL); - } - - /** - * Handle changes in groups that needs to be propagated to the NCP multicast table. - * - * XXX: Since Z2M doesn't explicitly check-in downstream when groups are created/removed, we look at outgoing genGroups commands. - * If the NCP doesn't know about groups, it can miss messages from some devices (remotes for example), so we add it... - * - * @param commandId - * @param groupId - */ - private async onGroupChange(commandId: number, groupId?: number): Promise { - switch (commandId) { - case Cluster.genGroups.commands.add.ID: { - // check if group already in multicast table, should not happen... - const existingIndex = this.multicastTable.findIndex((e) => ((e != null) && (e.multicastId === groupId))); - - if (existingIndex == -1) { - // find first unused index - const newEntryIndex = this.multicastTable.findIndex((e) => (!e)); - - if (newEntryIndex != -1) { - const newEntry: EmberMulticastTableEntry = { - multicastId: groupId, - endpoint: FIXED_ENDPOINTS[0].endpoint, - networkIndex: FIXED_ENDPOINTS[0].networkIndex, - }; - const status = (await this.ezsp.ezspSetMulticastTableEntry(newEntryIndex, newEntry)); - - if (status !== EmberStatus.SUCCESS) { - console.error( - `Failed to register group "${groupId}" in multicast table at index "${newEntryIndex}" with status=${EmberStatus[status]}.` - ); - } else { - debug(`Registered multicast table entry: ${JSON.stringify(newEntry)}.`); - } - - // always assume "it worked" to keep sync with Z2M first, NCP second, otherwise trouble might arise... should always work anyway - this.multicastTable[newEntryIndex] = newEntry; - } else { - console.warn(`Coordinator multicast table is full (max: ${STACK_CONFIGS[this.stackConfig].MULTICAST_TABLE_SIZE}). ` - + `Some devices in new groups may not work properly, including in group "${groupId}". ` - + `If that happens, please remove groups to be below the limit. ` - + `Removed groups are only removed from coordinator after a Zigbee2MQTT restart.`); - } - } else { - debug(`Added group "${groupId}", but local table says it is already registered at index "${existingIndex}". Skipping.`); - } - break; - } - // NOTE: Can't remove groups, since we watch from command exec to group members, that would trigger from any removed member, - // even though the group might still exist... - // Leaving this here (since it's done...), just in case we get better notifications for groups from upstream. - // case Cluster.genGroups.commands.remove.ID: { - // const entryIndex = this.multicastTable.findIndex((e) => ((e != null) && (e.multicastId === groupId))); - - // // just in case, never remove GP at i zero, should never be the case... - // if (entryIndex > 0) { - // const entry = this.multicastTable[entryIndex]; - // entry.endpoint = 0;// signals "not in use" in the stack - // const status = (await this.ezsp.ezspSetMulticastTableEntry(entryIndex, entry)); - - // if (status !== EmberStatus.SUCCESS) { - // console.error(`Failed to remove multicast table entry at index "${entryIndex}" for group "${groupId}".`); - // } else { - // debug(`Removed multicast table entry at index "${entryIndex}".`); - // } - - // // always assume "it worked" to keep sync with Z2M first, NCP second, otherwise trouble might arise... should always work anyway - // this.multicastTable[entryIndex] = null; - // } else { - // debug(`Removed group "${groupId}", but local table did not have a reference to it.`); - // } - // break; - // } - // case Cluster.genGroups.commands.removeAll.ID: { - // // this can create quite a few NCP calls, but hopefully shouldn't happen often - // // always skip green power at i==0 - // for (let i = 1; i < this.multicastTable.length; i++) { - // const entry = this.multicastTable[i]; - - // if (entry != null) { - // entry.endpoint = 0;// signals "not in use" in the stack - // const status = (await this.ezsp.ezspSetMulticastTableEntry(i, entry)); - - // if (status !== EmberStatus.SUCCESS) { - // console.error(`Failed to remove multicast entry at index "${i}" with status=${EmberStatus[status]}.`); - // } else { - // debug(`Removed multicast table entry at index "${i}".`); - // } - // } - - // this.multicastTable[i] = null; - // } - - // break; - // } - } - } - - //---- START Events - - //---- END Events - - //---- START Cache-enabled EZSP wrappers - - /** - * Clear the cached network values (set to invalid values). - */ - private clearNetworkCache(): void { - this.networkCache = initNetworkCache(); - } - - /** - * Return the current network state. - * This call caches the results on the host to prevent frequent EZSP transactions. - * Check against UNKNOWN_NETWORK_STATE for validity. - */ - public async emberNetworkState(): Promise { - if (this.networkCache.status === (UNKNOWN_NETWORK_STATE as EmberNetworkStatus)) { - const networkStatus = (await this.ezsp.ezspNetworkState()); - - this.networkCache.status = networkStatus; - } - - return this.networkCache.status; - } - - /** - * Return the EUI 64 of the local node - * This call caches the results on the host to prevent frequent EZSP transactions. - * Check against BLANK_EUI64 for validity. - */ - public async emberGetEui64(): Promise { - if (this.networkCache.eui64 === BLANK_EUI64) { - this.networkCache.eui64 = (await this.ezsp.ezspGetEui64()); - } - - return this.networkCache.eui64; - } - - /** - * Return the PAN ID of the local node. - * This call caches the results on the host to prevent frequent EZSP transactions. - * Check against INVALID_PAN_ID for validity. - */ - public async emberGetPanId(): Promise { - if (this.networkCache.parameters.panId === INVALID_PAN_ID) { - const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - - if (status === EmberStatus.SUCCESS) { - this.networkCache.parameters = parameters; - } else { - console.error(`Failed to get PAN ID (via network parameters) with status=${EmberStatus[status]}.`); - } - } - - return this.networkCache.parameters.panId; - } - - /** - * Return the Extended PAN ID of the local node. - * 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 { - if (equals(this.networkCache.parameters.extendedPanId, BLANK_EXTENDED_PAN_ID)) { - const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - - if (status === EmberStatus.SUCCESS) { - this.networkCache.parameters = parameters; - } else { - console.error(`Failed to get Extended PAN ID (via network parameters) with status=${EmberStatus[status]}.`); - } - } - - return this.networkCache.parameters.extendedPanId; - } - - /** - * Return the radio channel (uint8_t) of the current network. - * This call caches the results on the host to prevent frequent EZSP transactions. - * Check against INVALID_RADIO_CHANNEL for validity. - */ - public async emberGetRadioChannel(): Promise { - if (this.networkCache.parameters.radioChannel === INVALID_RADIO_CHANNEL) { - const [status, , parameters] = (await this.ezsp.ezspGetNetworkParameters()); - - if (status === EmberStatus.SUCCESS) { - this.networkCache.parameters = parameters; - } else { - console.error(`Failed to get radio channel (via network parameters) with status=${EmberStatus[status]}.`); - } - } - - return this.networkCache.parameters.radioChannel; - } - - // queued - public async emberStartEnergyScan(): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - const status = (await this.ezsp.ezspStartScan( - EzspNetworkScanType.ENERGY_SCAN, - EMBER_ALL_802_15_4_CHANNELS_MASK, - ENERGY_SCAN_DURATION, - )); - - if (status !== SLStatus.OK) { - console.error(`Failed energy scan request with status=${SLStatus[status]}.`); - return EmberStatus.ERR_FATAL; - } - - // TODO: result in logs only atm, since UI doesn't support it - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - //---- END Cache-enabled EZSP wrappers - - //---- START EZSP wrappers - - /** - * Ensure the Host & NCP are aligned on protocols using version. - * Cache the retrieved information. - * - * NOTE: currently throws on mismatch until support for lower versions is implemented (not planned atm) - * - * 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)); - - // 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}`); - } - - debug(`NCP info: EZSPVersion=${ncpEzspProtocolVer} StackType=${ncpStackType} StackVersion=${ncpStackVer}`); - - const [status, versionStruct] = (await this.ezsp.ezspGetVersionStruct()); - - if (status !== EzspStatus.SUCCESS) { - // NCP has old style version number - debug(`NCP has old-style version number.`); - this.version = { - ezsp: ncpEzspProtocolVer, - revision: `${ncpStackVer}`, - major: ncpStackVer, - minor: 0, - patch: 0, - special: 0, - build: 0, - type: EmberVersionType.GA,// default... - }; - } else { - // NCP has new style version number - this.version = { - ezsp: ncpEzspProtocolVer, - revision: `${versionStruct.major}.${versionStruct.minor}.${versionStruct.patch} [${EmberVersionType[versionStruct.type]}]`, - ...versionStruct, - }; - - if (versionStruct.type !== EmberVersionType.GA) { - console.warn(`NCP is running a non-GA version (${EmberVersionType[versionStruct.type]}).`); - } - } - - debug(`NCP version info: ${JSON.stringify(this.version)}`); - } - - /** - * This function sets an EZSP config value. - * WARNING: Do not call for values that cannot be set after init without first resetting NCP (like table sizes). - * To avoid an extra NCP call, this does not check for it. - * @param configId - * @param value uint16_t - * @returns - */ - private async emberSetEzspConfigValue(configId: EzspConfigId, value: number): Promise { - const status = (await this.ezsp.ezspSetConfigurationValue(configId, value)); - - debug(`[EzspConfigId] SET "${EzspConfigId[configId]}" TO "${value}" with status=${EzspStatus[status]}.`); - - if (status === EzspStatus.ERROR_INVALID_ID) { - // can be ZLL where not all NCPs need or support it. - console.warn(`[EzspConfigId] Unsupported configuration ID ${EzspConfigId[configId]} by NCP.`); - } else if (status !== EzspStatus.SUCCESS) { - // don't fail in case a set value gets called "out of time" - console.error(`[EzspConfigId] Failed to SET "${EzspConfigId[configId]}" TO "${value}" with status=${EzspStatus[status]}.`); - } - - return status; - } - - /** - * This function sets an EZSP value. - * @param valueId - * @param valueLength uint8_t - * @param value uint8_t * - * @returns - */ - private async emberSetEzspValue(valueId: EzspValueId, valueLength: number, value: number[]): Promise { - const status = (await this.ezsp.ezspSetValue(valueId, valueLength, value)); - - debug(`[EzspValueId] SET "${EzspValueId[valueId]}" TO "${value}" with status=${EzspStatus[status]}.`); - - return status; - } - - /** - * This function sets an EZSP policy. - * @param policyId - * @param decisionId Can be bitop - * @returns - */ - private async emberSetEzspPolicy(policyId: EzspPolicyId, decisionId: number): Promise { - const status = (await this.ezsp.ezspSetPolicy(policyId, decisionId)); - - debug(`[EzspPolicyId] SET "${EzspPolicyId[policyId]}" TO "${decisionId}" with status=${EzspStatus[status]}.`); - - return status; - } - - /** - * Here we convert the normal Ember AES hash call to the specialized EZSP call. - * This came about because we cannot pass a block of data that is - * both input and output into EZSP. The block must be broken up into two - * elements. We unify the two pieces here to make it invisible to the users. - * @param context EmberAesMmoHashContext * - * @param finalize - * @param data uint8_t * Expected of valid length (as in, not larger alloc) - * @returns status - * @returns result context or null - */ - private async aesMmoHash(context: EmberAesMmoHashContext, finalize: boolean, data: Buffer): - Promise<[EmberStatus, reContext: EmberAesMmoHashContext]> { - if (data.length > 255) { - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); - } - - const [status, reContext] = (await this.ezsp.ezspAesMmoHash(context, finalize, data)); - - return [status, reContext]; - } - - /** - * This routine processes the passed chunk of data and updates - * the hash calculation based on it. The data passed in MUST - * have a length that is a multiple of 16. - * - * @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 - * 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]> { - return this.aesMmoHash(context, false/*finalize?*/, data); - } - - /** - * This routine processes the passed chunk of data (if non-NULL) - * and update the hash context that is passed in. In then performs - * the final calculations on the hash and returns the final answer - * in the result parameter of the ::EmberAesMmoHashContext structure. - * The length of the data passed in may be any value, it does not have - * to be a multiple of 16. - * - * @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 - * 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]> { - return this.aesMmoHash(context, true/*finalize?*/, data); - } - - /** - * This is a convenience method when the hash data is less than 255 - * bytes. It inits, updates, and finalizes the hash in one function call. - * - * @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 - * 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]> { - const context = aesMmoHashInit(); - - const [status, reContext] = (await this.emberAesMmoHashFinal(context, data)); - - return [status, reContext?.result]; - } - - /** - * Enable local permit join and optionally broadcast the ZDO Mgmt_Permit_Join_req message. - * This API can be called from any device type and still return EMBER_SUCCESS. - * If the API is called from an end device, the permit association bit will just be left off. - * - * @param duration uint8_t The duration that the permit join bit will remain on - * and other devices will be able to join the current network. - * @param broadcastMgmtPermitJoin whether or not to broadcast the ZDO Mgmt_Permit_Join_req message. - * - * @returns status of whether or not permit join was enabled. - * @returns apsFrame Will be null if not broadcasting. - * @returns messageTag The tag passed to ezspSend${x} function. - */ - private async emberPermitJoining(duration: number, broadcastMgmtPermitJoin: boolean) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { - let status = (await this.ezsp.ezspPermitJoining(duration)); - let apsFrame: EmberApsFrame = null; - let messageTag: number = null; - - debug(`Permit joining for ${duration} sec. status=${[status]}`); - - if (broadcastMgmtPermitJoin) { - // `authentication`: TC significance always 1 (zb specs) - [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(EMBER_BROADCAST_ADDRESS, duration, 1, this.defaultApsOptions)); - } - - return [status, apsFrame, messageTag]; - } - - /** - * Set the trust center policy bitmask using decision. - * @param decision - * @returns - */ - private async emberSetJoinPolicy(decision: EmberJoinDecision): Promise { - let policy: number = EzspDecisionBitmask.DEFAULT_CONFIGURATION; - - if (decision == EmberJoinDecision.USE_PRECONFIGURED_KEY) { - policy = (EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS); - } else if (decision == EmberJoinDecision.SEND_KEY_IN_THE_CLEAR) { - policy = (EzspDecisionBitmask.ALLOW_JOINS | EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS | EzspDecisionBitmask.SEND_KEY_IN_CLEAR); - } else if (decision == EmberJoinDecision.ALLOW_REJOINS_ONLY) { - policy = EzspDecisionBitmask.ALLOW_UNSECURED_REJOINS; - } - - 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 { - debug(`Failed to get source route overhead (via extended value), status=${EzspStatus[status]}.`); - } - - 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 - - /** - * ZDO - * Change the default radius for broadcast ZDO requests - * - * @param radius uint8_t The radius to be used for future ZDO request broadcasts. - */ - private setZDORequestRadius(radius: number): void { - this.zdoRequestRadius = radius; - } - - /** - * ZDO - * Retrieve the default radius for broadcast ZDO requests - * - * @return uint8_t The radius to be used for future ZDO request broadcasts. - */ - private getZDORequestRadius(): number { - return this.zdoRequestRadius; - } - - /** - * ZDO - * Get the next device request sequence number. - * - * Requests have sequence numbers so that they can be matched up with the - * responses. To avoid complexities, the library uses numbers with the high - * bit clear and the stack uses numbers with the high bit set. - * - * @return uint8_t The next device request sequence number - */ - private nextZDORequestSequence(): number { - return (this.zdoRequestSequence = ((++this.zdoRequestSequence) & APPLICATION_ZDO_SEQUENCE_MASK)); - } - - /** - * ZDO - * - * @param destination - * @param clusterId uint16_t - * @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: EmberNodeId, clusterId: number, options: EmberApsOption): - Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { - if (this.zdoRequestBuffalo.getPosition() > EZSP_MAX_FRAME_LENGTH) { - return [EmberStatus.MESSAGE_TOO_LONG, null, null]; - } - - const messageTag = this.nextZDORequestSequence(); - - this.zdoRequestBuffalo.setCommandByte(0, messageTag); - - const apsFrame: EmberApsFrame = { - profileId: ZDO_PROFILE_ID, - clusterId: clusterId, - sourceEndpoint: ZDO_ENDPOINT, - destinationEndpoint: ZDO_ENDPOINT, - options: options, - groupId: 0, - sequence: 0,// set by stack - }; - const messageContents = this.zdoRequestBuffalo.getWritten(); - - if (destination === EMBER_BROADCAST_ADDRESS || destination === EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS - || destination === EMBER_SLEEPY_BROADCAST_ADDRESS) { - debug(`~~~> [ZDO BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`); - const [status, apsSequence] = (await this.ezsp.ezspSendBroadcast( - destination, - apsFrame, - this.getZDORequestRadius(), - messageTag, - messageContents, - )); - apsFrame.sequence = apsSequence; - - debug(`~~~> [SENT ZDO type=BROADCAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${EmberStatus[status]}]`); - return [status, apsFrame, messageTag]; - } else { - debug(`~~~> [ZDO UNICAST apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag}]`); - const [status, apsSequence] = (await this.ezsp.ezspSendUnicast( - EmberOutgoingMessageType.DIRECT, - destination, - apsFrame, - messageTag, - messageContents, - )); - apsFrame.sequence = apsSequence; - - debug(`~~~> [SENT ZDO type=DIRECT apsFrame=${JSON.stringify(apsFrame)} messageTag=${messageTag} status=${EmberStatus[status]}]`); - 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 EmberStatus 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]> { - // 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]; - } - - this.zdoRequestBuffalo.setPosition(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); - - debug(`~~~> [ZDO MATCH DESCRIPTOR target=${target} profile=${profile} inClusters=${inClusters} outClusters=${outClusters}]`); - return this.sendZDORequestBuffer(target, 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 ::EmberStatus 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(target); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - debug(`~~~> [ZDO NETWORK ADDRESS target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`); - return this.sendZDORequestBuffer(EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, 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 ::EmberStatus 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - debug(`~~~> [ZDO IEEE ADDRESS target=${target} reportKids=${reportKids} childStartIndex=${childStartIndex}]`); - return this.sendZDORequestBuffer(target, 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: EmberNodeId, reportKids: boolean, childStartIndex: number, - options: EmberApsOption, targetNodeIdOfRequest: EmberNodeId): Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(discoveryNodeId); - this.zdoRequestBuffalo.writeUInt8(reportKids ? 1 : 0); - this.zdoRequestBuffalo.writeUInt8(childStartIndex); - - debug(`~~~> [ZDO IEEE ADDRESS targetNodeIdOfRequest=${targetNodeIdOfRequest} discoveryNodeId=${discoveryNodeId} ` - + `reportKids=${reportKids} childStartIndex=${childStartIndex}]`); - return this.sendZDORequestBuffer(targetNodeIdOfRequest, IEEE_ADDRESS_REQUEST, options); - } - - /** - * ZDO - * - * @param target uint16_t - * @param clusterId uint16_t - * @param options - * @returns - */ - private async emberSendZigDevRequestTarget(target: EmberNodeId, clusterId: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(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 EmberStatus 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt16(target); - this.zdoRequestBuffalo.writeUInt8(targetEndpoint); - - debug(`~~~> [ZDO SIMPLE DESCRIPTOR target=${target} targetEndpoint=${targetEndpoint}]`); - return this.sendZDORequestBuffer(target, SIMPLE_DESCRIPTOR_REQUEST, options); - } - - /** - * ZDO - * @brief 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 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 the - * ::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. - * - * @return An ::EmberStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * _ ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - * @param target - * @param bindClusterId - * @param source - * @param sourceEndpoint uint8_t - * @param clusterId uint16_t - * @param type uint8_t - * @param destination - * @param groupAddress uint16_t - * @param destinationEndpoint uint8_t - * @param options - */ - 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(source); - this.zdoRequestBuffalo.writeUInt8(sourceEndpoint); - this.zdoRequestBuffalo.writeUInt16(clusterId); - this.zdoRequestBuffalo.writeUInt8(type); - - switch (type) { - case UNICAST_BINDING: - this.zdoRequestBuffalo.writeIeeeAddr(destination); - this.zdoRequestBuffalo.writeUInt8(destinationEndpoint); - break; - case MULTICAST_BINDING: - this.zdoRequestBuffalo.writeUInt16(groupAddress); - break; - default: - return [EmberStatus.ERR_FATAL, 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. - * - * @return An EmberStatus value. ::EMBER_SUCCESS, ::EMBER_NO_BUFFERS, - * ::EMBER_NETWORK_DOWN or ::EMBER_NETWORK_BUSY. - */ - 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]> { - debug(`~~~> [ZDO BIND target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` - + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`); - return this.emberSendZigDevBindRequest( - target, - 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. - * - * @return An ::EmberStatus value. - * - ::EMBER_SUCCESS - * - ::EMBER_NO_BUFFERS - * _ ::EMBER_NETWORK_DOWN - * - ::EMBER_NETWORK_BUSY - */ - 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]> { - debug(`~~~> [ZDO UNBIND target=${target} source=${source} sourceEndpoint=${sourceEndpoint} clusterId=${clusterId} type=${type} ` - + `destination=${destination} groupAddress=${groupAddress} destinationEndpoint=${destinationEndpoint}]`); - return this.emberSendZigDevBindRequest( - target, - 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 EmberStatus 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]> { - debug(`~~~> [ZDO ACTIVE ENDPOINTS target=${target}]`); - return this.emberSendZigDevRequestTarget(target, 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 EmberStatus 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]> { - debug(`~~~> [ZDO POWER DESCRIPTOR target=${target}]`); - return this.emberSendZigDevRequestTarget(target, 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 ::EmberStatus 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]> { - debug(`~~~> [ZDO NODE DESCRIPTOR target=${target}]`); - return this.emberSendZigDevRequestTarget(target, 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 EmberStatus 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]> { - debug(`~~~> [ZDO LQI TABLE target=${target} startIndex=${startIndex}]`); - return this.emberTableRequest(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 EmberStatus 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]> { - debug(`~~~> [ZDO ROUTING TABLE target=${target} startIndex=${startIndex}]`); - return this.emberTableRequest(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 EmberStatus 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]> { - debug(`~~~> [ZDO BINDING TABLE target=${target} startIndex=${startIndex}]`); - return this.emberTableRequest(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: EmberNodeId, startIndex: number, options: EmberApsOption) - : Promise<[EmberStatus, apsFrame: EmberApsFrame, messageTag: number]> { - this.zdoRequestBuffalo.setPosition(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 EmberStatus 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeIeeeAddr(deviceAddress); - this.zdoRequestBuffalo.writeUInt8(leaveRequestFlags); - - debug(`~~~> [ZDO LEAVE target=${target} deviceAddress=${deviceAddress} leaveRequestFlags=${leaveRequestFlags}]`); - return this.sendZDORequestBuffer(target, 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 EmberStatus 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]> { - this.zdoRequestBuffalo.setPosition(ZDO_MESSAGE_OVERHEAD); - - this.zdoRequestBuffalo.writeUInt8(duration); - this.zdoRequestBuffalo.writeUInt8(authentication); - - debug(`~~~> [ZDO PERMIT JOINING target=${target} duration=${duration} authentication=${authentication}]`); - return this.sendZDORequestBuffer(target, PERMIT_JOINING_REQUEST, options); - } - - //---- END Ember ZDO - - //-- START Adapter implementation - - public static async isValidPath(path: string): Promise { - // For TCP paths we cannot get device information, therefore we cannot validate it. - if (SocketPortUtils.isTcpPath(path)) { - return false; - } - - try { - return SerialPortUtils.is(RealpathSync(path), autoDetectDefinitions); - } catch (error) { - debug(`Failed to determine if path is valid: '${error}'`); - return false; - } - } - - public static async autoDetectPath(): Promise { - const paths = await SerialPortUtils.find(autoDetectDefinitions); - paths.sort((a, b) => (a < b) ? -1 : 1); - return paths.length > 0 ? paths[0] : null; - } - - public async start(): Promise { - console.log(`======== Ember Adapter Starting ========`); - this.initVariables(); - - debug(`Starting EZSP with stack configuration: "${this.stackConfig}".`); - const result = await this.initEzsp(); - - return result; - } - - public async stop(): Promise { - await this.ezsp.stop(); - - this.initVariables(); - console.log(`======== Ember Adapter Stopped ========`); - } - - // queued, non-InterPAN - public async getCoordinator(): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - // in all likelihood this will be retrieved from cache - const ieeeAddr = (await this.emberGetEui64()); - - resolve({ - ieeeAddr, - networkAddress: ZIGBEE_COORDINATOR_ADDRESS, - manufacturerID: MANUFACTURER_CODE, - endpoints: FIXED_ENDPOINTS.map((ep) => { - return { - profileID: ep.profileId, - ID: ep.endpoint, - deviceID: ep.deviceId, - inputClusters: ep.inClusterList, - outputClusters: ep.outClusterList, - }; - }), - }); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - public async getCoordinatorVersion(): Promise { - return {type: `EZSP v${this.version.ezsp}`, meta: this.version}; - } - - // queued - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async reset(type: "soft" | "hard"): Promise { - return Promise.reject(new Error("Not supported")); - // NOTE: although this function is legacy atm, a couple of new untested EZSP functions that could also prove useful: - // this.ezsp.ezspTokenFactoryReset(true/*excludeOutgoingFC*/, true/*excludeBootCounter*/); - // this.ezsp.ezspResetNode() - } - - public async supportsBackup(): Promise { - return true; - } - - // queued - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async backup(ieeeAddressesInDatabase: string[]): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - // grab fresh version here, bypass cache - const [netStatus, , netParams] = (await this.ezsp.ezspGetNetworkParameters()); - - if (netStatus !== EmberStatus.SUCCESS) { - console.error(`[BACKUP] Failed to get network parameters.`); - return netStatus; - } - - // update cache - this.networkCache.parameters = netParams; - this.networkCache.eui64 = (await this.ezsp.ezspGetEui64()); - - const [netKeyStatus, netKeyInfo] = (await this.ezsp.ezspGetNetworkKeyInfo()); - - if (netKeyStatus !== SLStatus.OK) { - console.error(`[BACKUP] Failed to get network keys info.`); - return ((netKeyStatus === SLStatus.BUSY) || (netKeyStatus === SLStatus.NOT_READY)) - ? EmberStatus.NETWORK_BUSY : EmberStatus.ERR_FATAL;// allow retry on statuses that should be temporary - } - - if (!netKeyInfo.networkKeySet) { - throw new Error(`[BACKUP] No network key set.`); - } - - let keyList: LinkKeyBackupData[] = []; - - if (STACK_CONFIGS[this.stackConfig].KEY_TABLE_SIZE) { - 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() - // )); - // console.log(tokensBuf.toString('hex')); - - let context: SecManContext = initSecurityManagerContext(); - context.coreKeyType = SecManKeyType.TC_LINK; - const [tcLinkKey, tclkStatus] = (await this.ezsp.ezspExportKey(context)); - - if (tclkStatus !== SLStatus.OK) { - throw new Error(`[BACKUP] Failed to export TC Link Key with status=${SLStatus[tclkStatus]}.`); - } - - context = initSecurityManagerContext();// make sure it's back to zeroes - context.coreKeyType = SecManKeyType.NETWORK; - context.keyIndex = 0; - const [networkKey, nkStatus] = (await this.ezsp.ezspExportKey(context)); - - if (nkStatus !== SLStatus.OK) { - throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`); - } - - const zbChannels = Array.from(Array(EMBER_NUM_802_15_4_CHANNELS), (e, i)=> i + EMBER_MIN_802_15_4_CHANNEL_NUMBER); - - resolve({ - networkOptions: { - panId: netParams.panId,// uint16_t - extendedPanId: Buffer.from(netParams.extendedPanId), - channelList: zbChannels.map((c: number) => ((2 ** c) & netParams.channels) ? c : null).filter((x) => x), - networkKey: networkKey.contents, - networkKeyDistribute: false, - }, - logicalChannel: netParams.radioChannel, - networkKeyInfo: { - sequenceNumber: netKeyInfo.networkKeySequenceNumber, - frameCounter: netKeyInfo.networkKeyFrameCounter, - }, - securityLevel: STACK_CONFIGS[this.stackConfig].SECURITY_LEVEL, - networkUpdateId: netParams.nwkUpdateId, - coordinatorIeeeAddress: Buffer.from(this.networkCache.eui64.substring(2)/*take out 0x*/, 'hex').reverse(), - devices: keyList.map((key) => ({ - networkAddress: null,// not used for restore, no reason to make NCP calls for nothing - ieeeAddress: Buffer.from(key.deviceEui64.substring(2)/*take out 0x*/, 'hex').reverse(), - isDirectChild: false,// not used - linkKey: { - key: key.key.contents, - rxCounter: key.incomingFrameCounter, - txCounter: key.outgoingFrameCounter, - }, - })), - ezsp: { - version: this.version.ezsp, - hashed_tclk: tcLinkKey.contents, - // tokens: tokensBuf.toString('hex'), - // altNetworkKey: altNetworkKey.contents, - } - }); - - return EmberStatus.SUCCESS; - }, - reject, - true,// takes prio - ); - }); - - } - - // queued, non-InterPAN - public async getNetworkParameters(): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - // first call will cache for the others, but in all likelihood, it will all be from freshly cached after init - // since Controller caches this also. - const panID = (await this.emberGetPanId()); - const extendedPanID = (await this.emberGetExtendedPanId()); - const channel = (await this.emberGetRadioChannel()); - - resolve({ - panID: panID, - extendedPanID: parseInt(Buffer.from(extendedPanID).toString('hex'), 16), - channel: channel, - }); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued - public async setTransmitPower(value: number): Promise { - if (typeof value !== 'number') { - console.error(`Tried to set transmit power to non-number. Value ${value} of type ${typeof value}.`); - return; - } - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - const status = await this.ezsp.ezspSetRadioPower(value); - - if (status !== EmberStatus.SUCCESS) { - console.error(`Failed to set transmit power to ${value} status=${EmberStatus[status]}.`); - return status; - } - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued - public async addInstallCode(ieeeAddress: string, key: Buffer): Promise { - if (!key) { - throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; no code given.`); - } - - let validInstallCodeSize = false; - - for (const validCodeSize of EMBER_INSTALL_CODE_SIZES) { - if (key.length === validCodeSize) { - validInstallCodeSize = true; - break; - } - } - - if (!validInstallCodeSize) { - throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; invalid code size.`); - } - - // Reverse the bits in a byte - const reverse = (b: number): number => { - return ((b * 0x0802 & 0x22110) | (b * 0x8020 & 0x88440)) * 0x10101 >> 16; - }; - let crc = 0xFFFF;// uint16_t - - // Compute the CRC and verify that it matches. - // The bit reversals, byte swap, and ones' complement are due to differences between halCommonCrc16 and the Smart Energy version. - for (let index = 0; index < (key.length - EMBER_INSTALL_CODE_CRC_SIZE); index++) { - crc = halCommonCrc16(reverse(key[index]), crc); - } - - crc = ~highLowToInt(reverse(lowByte(crc)), reverse(highByte(crc))); - - if (key[key.length - EMBER_INSTALL_CODE_CRC_SIZE] !== lowByte(crc) || key[key.length - EMBER_INSTALL_CODE_CRC_SIZE + 1] !== highByte(crc)) { - throw new Error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}"; invalid code CRC.`); - } - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - // Compute the key from the install code and CRC. - const [aesStatus, keyContents] = (await this.emberAesHashSimple(key)); - - if (aesStatus !== EmberStatus.SUCCESS) { - console.error(`[ADD INSTALL CODE] Failed AES hash for "${ieeeAddress}" with status=${EmberStatus[aesStatus]}.`); - 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)); - - if (impStatus == SLStatus.OK) { - debug(`[ADD INSTALL CODE] Success for "${ieeeAddress}".`); - } else { - console.error(`[ADD INSTALL CODE] Failed for "${ieeeAddress}" with status=${SLStatus[impStatus]}.`); - return EmberStatus.ERR_FATAL; - } - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - /** WARNING: Adapter impl. Starts timer immediately upon returning */ - public waitFor(networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, transactionSequenceNumber: number, - clusterID: number, commandIdentifier: number, timeout: number): {promise: Promise; cancel: () => void;} { - const waiter = this.oneWaitress.waitFor({ - target: networkAddress, - apsFrame: { - clusterId: clusterID, - profileId: HA_PROFILE_ID,// XXX: ok? only used by OTA upstream - sequence: 0,// set by stack - sourceEndpoint: endpoint, - destinationEndpoint: 0, - groupId: 0, - options: EmberApsOption.NONE, - }, - zclSequence: transactionSequenceNumber, - }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT * 3);// XXX: since this is used by OTA..? - - return { - cancel: (): void => this.oneWaitress.remove(waiter.id), - promise: waiter.start().promise, - }; - } - - //---- ZDO - - // queued, non-InterPAN - public async permitJoin(seconds: number, networkAddress: number): Promise { - const preJoining = async (): Promise => { - if (seconds) { - const plaintextKey: SecManKey = {contents: Buffer.from(ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY)}; - const impKeyStatus = (await this.ezsp.ezspImportTransientKey(BLANK_EUI64, plaintextKey, SecManFlag.NONE)); - - debug(`[ZDO] Pre joining import transient key status=${SLStatus[impKeyStatus]}.`); - return impKeyStatus === SLStatus.OK ? EmberStatus.SUCCESS : EmberStatus.ERR_FATAL; - } else { - await this.ezsp.ezspClearTransientLinkKeys(); - - const setJPstatus = (await this.emberSetJoinPolicy(EmberJoinDecision.ALLOW_REJOINS_ONLY)); - - if (setJPstatus !== EzspStatus.SUCCESS) { - console.error(`[ZDO] Failed set join policy for with status=${EzspStatus[setJPstatus]}.`); - return EmberStatus.ERR_FATAL; - } - - return EmberStatus.SUCCESS; - } - }; - - // NOTE: can't ZDO PJ on coordinator, so if network address is null or zero (coordinator), using local permit join - if (networkAddress) { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - const pjStatus = (await preJoining()); - - if (pjStatus !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${EmberStatus[pjStatus]}.`); - return pjStatus; - } - - // `authentication`: TC significance always 1 (zb specs) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberPermitJoiningRequest(networkAddress, seconds, 1, 0)); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed permit joining request for "${networkAddress}" with status=${EmberStatus[status]}.`); - return status; - } - - (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: PERMIT_JOINING_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } else { - // no device specified to open, open coordinator + broadcast - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - const pjStatus = (await preJoining()); - - if (pjStatus !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed pre joining request for "${networkAddress}" with status=${EmberStatus[pjStatus]}.`); - return pjStatus; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, apsFrame, messageTag] = (await this.emberPermitJoining(seconds, true/*broadcast*/)); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed permit joining request with status=${EmberStatus[status]}.`); - return status; - } - - // NOTE: because Z2M is refreshing the permit join duration early to prevent it from closing - // (every 200sec, even if only opened for 254sec), we can't wait for the stack opened status, - // as it won't trigger again if already opened... so instead we assume it worked - // NOTE2: with EZSP, 255=forever, and 254=max, but since upstream logic uses fixed 254 with interval refresh, - // we can't simply bypass upstream calls if called for "forever" to prevent useless NCP calls (3-4 each time), - // until called with 0 (disable), since we don't know if it was requested for forever or not... - // TLDR: upstream logic change required to allow this - // if (seconds) { - // await this.oneWaitress.startWaitingForEvent( - // {eventName: OneWaitressEvents.STACK_STATUS_NETWORK_OPENED}, - // DEFAULT_ZCL_REQUEST_TIMEOUT, - // '[ZDO] Permit Joining', - // ); - // } else { - // // NOTE: CLOSED stack status is not triggered if the network was not OPENED in the first place, so don't wait for it - // // same kind of problem as described above (upstream always tries to close after start, but EZSP already is) - // } - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - } - - // queued, non-InterPAN - public async lqi(networkAddress: number): Promise { - const neighbors: TsType.LQINeighbor[] = []; - - const request = async (startIndex: number): Promise<[EmberStatus, tableEntries: number, entryCount: number]> => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [reqStatus, apsFrame, messageTag] = (await this.emberLqiTableRequest(networkAddress, startIndex, this.defaultApsOptions)); - - if (reqStatus !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed LQI request for "${networkAddress}" (index "${startIndex}") with status=${EmberStatus[reqStatus]}.`); - return [reqStatus, null, null]; - } - - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: LQI_TABLE_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - for (const entry of result.entryList) { - neighbors.push({ - ieeeAddr: entry.eui64, - networkAddress: entry.nodeId, - linkquality: entry.lqi, - relationship: entry.relationship, - depth: entry.depth, - }); - } - - return [EmberStatus.SUCCESS, result.neighborTableEntries, result.entryList.length]; - }; - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - let [status, tableEntries, entryCount] = (await request(0)); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - const size = tableEntries; - let nextStartIndex = entryCount; - - while (neighbors.length < size) { - [status, tableEntries, entryCount] = (await request(nextStartIndex)); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - nextStartIndex += entryCount; - } - - resolve({neighbors}); - return status; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async routingTable(networkAddress: number): Promise { - const table: TsType.RoutingTableEntry[] = []; - - const request = async (startIndex: number): Promise<[EmberStatus, tableEntries: number, entryCount: number]> => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [reqStatus, apsFrame, messageTag] = (await this.emberRoutingTableRequest(networkAddress, startIndex, this.defaultApsOptions)); - - if (reqStatus !== EmberStatus.SUCCESS) { - console.error( - `[ZDO] Failed routing table request for "${networkAddress}" (index "${startIndex}") with status=${EmberStatus[reqStatus]}.` - ); - return [reqStatus, null, null]; - } - - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: ROUTING_TABLE_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - for (const entry of result.entryList) { - table.push({ - destinationAddress: entry.destinationAddress, - status: RoutingTableStatus[entry.status],// get str value from enum to satisfy upstream's needs - nextHop: entry.nextHopAddress, - }); - } - - return [EmberStatus.SUCCESS, result.routingTableEntries, result.entryList.length]; - }; - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - let [status, tableEntries, entryCount] = (await request(0)); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - const size = tableEntries; - let nextStartIndex = entryCount; - - while (table.length < size) { - [status, tableEntries, entryCount] = (await request(nextStartIndex)); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - nextStartIndex += entryCount; - } - - resolve({table}); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async nodeDescriptor(networkAddress: number): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - /* eslint-disable @typescript-eslint/no-unused-vars */ - const [status, apsFrame, messageTag] = (await this.emberNodeDescriptorRequest(networkAddress, this.defaultApsOptions)); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed node descriptor for "${networkAddress}" with status=${EmberStatus[status]}.`); - return status; - } - - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: NODE_DESCRIPTOR_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - let type: TsType.DeviceType = 'Unknown'; - - switch (result.logicalType) { - case 0x0: - type = 'Coordinator'; - break; - case 0x1: - type = 'Router'; - break; - case 0x2: - type = 'EndDevice'; - break; - } - - // always 0 before rev. 21 where field was added - if (result.stackRevision < CURRENT_ZIGBEE_SPEC_REVISION) { - console.warn(`[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}).`); - } - - resolve({type, manufacturerCode: result.manufacturerCode}); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async activeEndpoints(networkAddress: 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.emberActiveEndpointsRequest(networkAddress, this.defaultApsOptions)); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed active endpoints request for "${networkAddress}" with status=${EmberStatus[status]}.`); - return status; - } - - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: ACTIVE_ENDPOINTS_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - resolve({endpoints: result.endpointList}); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async simpleDescriptor(networkAddress: number, endpointID: 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.emberSimpleDescriptorRequest( - networkAddress, - endpointID, - this.defaultApsOptions - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed simple descriptor request for "${networkAddress}" endpoint "${endpointID}" ` - + `with status=${EmberStatus[status]}.`); - return status; - } - - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: SIMPLE_DESCRIPTOR_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT)); - - resolve({ - profileID: result.profileId, - endpointID: result.endpoint, - deviceID: result.deviceId, - inputClusters: result.inClusterList, - outputClusters: result.outClusterList, - }); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async bind(destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, clusterID: number, - destinationAddressOrGroup: string | number, type: "endpoint" | "group", destinationEndpoint?: number): Promise { - if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') { - // dest address is EUI64 (str), so type should always be endpoint (unicast) - 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.emberBindRequest( - destinationNetworkAddress, - sourceIeeeAddress, - sourceEndpoint, - clusterID, - UNICAST_BINDING, - destinationAddressOrGroup, - null,// doesn't matter - destinationEndpoint, - this.defaultApsOptions, - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` - + `endpoint "${destinationEndpoint}" with status=${EmberStatus[status]}.`); - return status; - } - - await this.oneWaitress.startWaitingFor({ - target: destinationNetworkAddress, - apsFrame, - responseClusterId: BIND_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT); - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } else if (typeof destinationAddressOrGroup === 'number' && type === 'group') { - // dest is group num, so type should always be group (multicast) - 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.emberBindRequest( - destinationNetworkAddress, - sourceIeeeAddress, - sourceEndpoint, - clusterID, - MULTICAST_BINDING, - null,// doesn't matter - destinationAddressOrGroup, - destinationEndpoint,// doesn't matter - this.defaultApsOptions, - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed bind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` - + `with status=${EmberStatus[status]}.`); - return status; - } - - await this.oneWaitress.startWaitingFor({ - target: destinationNetworkAddress, - apsFrame, - responseClusterId: BIND_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT); - - resolve(); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - } - - // queued, non-InterPAN - public async unbind(destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, clusterID: number, - destinationAddressOrGroup: string | number, type: "endpoint" | "group", destinationEndpoint: number): Promise { - if (typeof destinationAddressOrGroup === 'string' && type === 'endpoint') { - // dest address is EUI64 (str), so type should always be endpoint (unicast) - 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.emberUnbindRequest( - destinationNetworkAddress, - sourceIeeeAddress, - sourceEndpoint, - clusterID, - UNICAST_BINDING, - destinationAddressOrGroup, - null,// doesn't matter - destinationEndpoint, - this.defaultApsOptions, - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" destination "${destinationAddressOrGroup}" ` - + `endpoint "${destinationEndpoint}" with status=${EmberStatus[status]}.`); - return status; - } - - await this.oneWaitress.startWaitingFor({ - target: destinationNetworkAddress, - apsFrame, - responseClusterId: UNBIND_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT); - - resolve(); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } else if (typeof destinationAddressOrGroup === 'number' && type === 'group') { - // dest is group num, so type should always be group (multicast) - 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.emberUnbindRequest( - destinationNetworkAddress, - sourceIeeeAddress, - sourceEndpoint, - clusterID, - MULTICAST_BINDING, - null,// doesn't matter - destinationAddressOrGroup, - destinationEndpoint,// doesn't matter - this.defaultApsOptions, - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed unbind request for "${destinationNetworkAddress}" group "${destinationAddressOrGroup}" ` - + `with status=${EmberStatus[status]}.`); - return status; - } - - await this.oneWaitress.startWaitingFor({ - target: destinationNetworkAddress, - apsFrame, - responseClusterId: UNBIND_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT); - - resolve(); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - } - - // queued, non-InterPAN - public async removeDevice(networkAddress: number, ieeeAddr: string): 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.emberLeaveRequest( - networkAddress, - ieeeAddr, - EmberLeaveRequestFlags.WITHOUT_REJOIN, - this.defaultApsOptions - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`[ZDO] Failed remove device request for "${networkAddress}" target "${ieeeAddr}" ` - + `with status=${EmberStatus[status]}.`); - return status; - } - - await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - responseClusterId: LEAVE_RESPONSE, - }, DEFAULT_ZDO_REQUEST_TIMEOUT); - - resolve(); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - //---- ZCL - - // queued, non-InterPAN - public async sendZclFrameToEndpoint(ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, - disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number): Promise { - const sourceEndpointInfo = typeof sourceEndpoint === 'number' ? - FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : FIXED_ENDPOINTS[0]; - const command = zclFrame.getCommand(); - let commandResponseId: number = null; - - if (command.hasOwnProperty('response') && disableResponse === false) { - commandResponseId = command.response; - } else if (!zclFrame.Header.frameControl.disableDefaultResponse) { - commandResponseId = Foundation.defaultRsp.ID; - } - - const apsFrame: EmberApsFrame = { - profileId: sourceEndpointInfo.profileId, - clusterId: zclFrame.Cluster.ID, - sourceEndpoint: sourceEndpointInfo.endpoint, - destinationEndpoint: (typeof endpoint === 'number') ? endpoint : FIXED_ENDPOINTS[0].endpoint, - options: this.defaultApsOptions, - groupId: 0, - sequence: 0,// set by stack - }; - - // don't RETRY if no response expected - if (commandResponseId == null) { - apsFrame.options &= ~EmberApsOption.RETRY; - } - - const data = zclFrame.toBuffer(); - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - 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 - } - } - - // track group changes in NCP multicast table - if (apsFrame.clusterId === Cluster.genGroups.ID) { - await this.onGroupChange(command.ID, zclFrame.Payload.groupid); - } - - debug(`~~~> [ZCL to=${networkAddress} apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, messageTag] = (await this.ezsp.send( - EmberOutgoingMessageType.DIRECT, - networkAddress, - apsFrame, - data, - 0,// alias - 0,// alias seq - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`~x~> [ZCL to=${networkAddress}] Failed to send request with status=${EmberStatus[status]}.`); - return status;// let queue handle retry based on status - } - - if (commandResponseId != null) { - // NOTE: aps sequence number will have been set by send function - const result = (await this.oneWaitress.startWaitingFor({ - target: networkAddress, - apsFrame, - zclSequence: zclFrame.Header.transactionSequenceNumber, - }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT)); - - resolve(result); - } else { - resolve(null);// don't expect a response - return EmberStatus.SUCCESS; - } - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame, sourceEndpoint?: number): Promise { - const sourceEndpointInfo = typeof sourceEndpoint === 'number' ? - FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : FIXED_ENDPOINTS[0]; - const apsFrame: EmberApsFrame = { - profileId: sourceEndpointInfo.profileId, - clusterId: zclFrame.Cluster.ID, - sourceEndpoint: sourceEndpointInfo.endpoint, - destinationEndpoint: FIXED_ENDPOINTS[0].endpoint, - options: this.defaultApsOptions, - groupId: groupID, - sequence: 0,// set by stack - }; - const data = zclFrame.toBuffer(); - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - 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 - } - } - - debug(`~~~> [ZCL GROUP apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`); - // 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, - data, - 0,// alias - 0,// alias seq - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`~x~> [ZCL GROUP] Failed to send with status=${EmberStatus[status]}.`); - 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; - }, - reject, - ); - }); - } - - // queued, non-InterPAN - public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { - const sourceEndpointInfo = typeof sourceEndpoint === 'number' ? - FIXED_ENDPOINTS.find((epi) => (epi.endpoint === sourceEndpoint)) : FIXED_ENDPOINTS[0]; - const apsFrame: EmberApsFrame = { - profileId: sourceEndpointInfo.profileId, - clusterId: zclFrame.Cluster.ID, - sourceEndpoint: sourceEndpointInfo.endpoint, - destinationEndpoint: (typeof endpoint === 'number') ? endpoint : FIXED_ENDPOINTS[0].endpoint, - options: this.defaultApsOptions, - groupId: EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, - sequence: 0,// set by stack - }; - const data = zclFrame.toBuffer(); - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.checkInterpanLock(); - - if (CHECK_APS_PAYLOAD_LENGTH) { - const maxPayloadLength = ( - await this.maximumApsPayloadLength(EmberOutgoingMessageType.BROADCAST, EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, apsFrame) - ); - - if (data.length > maxPayloadLength) { - return EmberStatus.MESSAGE_TOO_LONG;// queue will reject - } - } - - debug(`~~~> [ZCL BROADCAST apsFrame=${JSON.stringify(apsFrame)} header=${JSON.stringify(zclFrame.Header)}]`); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [status, messageTag] = (await this.ezsp.send( - EmberOutgoingMessageType.BROADCAST, - EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS, - apsFrame, - data, - 0,// alias - 0,// alias seq - )); - - if (status !== EmberStatus.SUCCESS) { - console.error(`~x~> [ZCL BROADCAST] Failed to send with status=${EmberStatus[status]}.`); - 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; - }, - reject, - ); - }); - } - - //---- InterPAN for Touchlink - // XXX: There might be a better way to handle touchlink with ZLL ezsp functions, but I don't have any device to test so, didn't look into it... - // TODO: check all this touchlink/interpan stuff - - // queued - public async setChannelInterPAN(channel: number): Promise { - if (typeof channel !== 'number') { - console.error(`Tried to set channel InterPAN to non-number. Channel ${channel} of type ${typeof channel}.`); - return; - } - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - this.interpanLock = true; - const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(channel)); - - if (status !== EmberStatus.SUCCESS) { - this.interpanLock = false;// XXX: ok? - console.error(`Failed to set InterPAN channel to ${channel} with status=${EmberStatus[status]}.`); - return status; - } - - resolve(); - return status; - }, - reject, - ); - }); - } - - // queued - public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddress: string): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - const msgBuffalo = new EzspBuffalo(Buffer.alloc(MAXIMUM_INTERPAN_LENGTH)); - - // cache-enabled getters - const sourcePanId = (await this.emberGetPanId()); - const sourceEui64 = (await this.emberGetEui64()); - - msgBuffalo.writeUInt16((LONG_DEST_FRAME_CONTROL | MAC_ACK_REQUIRED));// macFrameControl - msgBuffalo.writeUInt8(0);// sequence Skip Sequence number, stack sets the sequence number. - msgBuffalo.writeUInt16(INVALID_PAN_ID);// destPanId - msgBuffalo.writeIeeeAddr(ieeeAddress);// destAddress (longAddress) - msgBuffalo.writeUInt16(sourcePanId);// sourcePanId - msgBuffalo.writeIeeeAddr(sourceEui64);// sourceAddress - msgBuffalo.writeUInt16(STUB_NWK_FRAME_CONTROL);// nwkFrameControl - msgBuffalo.writeUInt8((EmberInterpanMessageType.UNICAST | INTERPAN_APS_FRAME_TYPE));// apsFrameControl - msgBuffalo.writeUInt16(zclFrame.Cluster.ID); - msgBuffalo.writeUInt16(TOUCHLINK_PROFILE_ID); - - debug(`~~~> [ZCL TOUCHLINK to=${ieeeAddress} header=${JSON.stringify(zclFrame.Header)}]`); - const status = (await this.ezsp.ezspSendRawMessage(Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]))); - - if (status !== EmberStatus.SUCCESS) { - console.error(`~x~> [ZCL TOUCHLINK to=${ieeeAddress}] Failed to send with status=${EmberStatus[status]}.`); - return status; - } - - // NOTE: can use ezspRawTransmitCompleteHandler if needed here - - resolve(); - return status; - }, - reject, - ); - }); - } - - // queued - public async sendZclFrameInterPANBroadcast(zclFrame: ZclFrame, timeout: number): Promise { - const command = zclFrame.getCommand(); - - if (!command.hasOwnProperty('response')) { - throw new Error(`Command '${command.name}' has no response, cannot wait for response.`); - } - - // just for waitress - const apsFrame: EmberApsFrame = { - profileId: TOUCHLINK_PROFILE_ID, - clusterId: zclFrame.Cluster.ID, - sourceEndpoint: 0, - destinationEndpoint: 0, - options: EmberApsOption.NONE, - groupId: EMBER_SLEEPY_BROADCAST_ADDRESS, - sequence: 0,// set by stack - }; - - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - const msgBuffalo = new EzspBuffalo(Buffer.alloc(MAXIMUM_INTERPAN_LENGTH)); - - // cache-enabled getters - const sourcePanId = (await this.emberGetPanId()); - const sourceEui64 = (await this.emberGetEui64()); - - msgBuffalo.writeUInt16(SHORT_DEST_FRAME_CONTROL);// macFrameControl - msgBuffalo.writeUInt8(0);// sequence Skip Sequence number, stack sets the sequence number. - msgBuffalo.writeUInt16(INVALID_PAN_ID);// destPanId - msgBuffalo.writeUInt16(apsFrame.groupId);// destAddress (longAddress) - msgBuffalo.writeUInt16(sourcePanId);// sourcePanId - msgBuffalo.writeIeeeAddr(sourceEui64);// sourceAddress - msgBuffalo.writeUInt16(STUB_NWK_FRAME_CONTROL);// nwkFrameControl - msgBuffalo.writeUInt8((EmberInterpanMessageType.BROADCAST | INTERPAN_APS_FRAME_TYPE));// apsFrameControl - msgBuffalo.writeUInt16(apsFrame.clusterId); - msgBuffalo.writeUInt16(apsFrame.profileId); - - const data = Buffer.concat([msgBuffalo.getWritten(), zclFrame.toBuffer()]); - - debug(`~~~> [ZCL TOUCHLINK BROADCAST header=${JSON.stringify(zclFrame.Header)}]`); - const status = (await this.ezsp.ezspSendRawMessage(data)); - - if (status !== EmberStatus.SUCCESS) { - console.error(`~x~> [ZCL TOUCHLINK BROADCAST] Failed to send with status=${EmberStatus[status]}.`); - return status; - } - - // NOTE: can use ezspRawTransmitCompleteHandler if needed here - - const result = (await this.oneWaitress.startWaitingFor({ - target: null, - apsFrame: apsFrame, - zclSequence: zclFrame.Header.transactionSequenceNumber, - }, timeout || DEFAULT_ZCL_REQUEST_TIMEOUT * 2));// XXX: touchlink timeout? - - resolve(result); - - return EmberStatus.SUCCESS; - }, - reject, - ); - }); - } - - // queued - public async restoreChannelInterPAN(): Promise { - return new Promise((resolve, reject): void => { - this.requestQueue.enqueue( - async (): Promise => { - const status = (await this.ezsp.ezspSetLogicalAndRadioChannel(this.networkOptions.channelList[0])); - - if (status !== EmberStatus.SUCCESS) { - console.error( - `Failed to restore InterPAN channel to ${this.networkOptions.channelList[0]} with status=${EmberStatus[status]}.` - ); - return status; - } - - // let adapter settle down - await Wait(3000); - - this.interpanLock = false; - - resolve(); - return status; - }, - reject, - ); - }); - } - - //-- END Adapter implementation - - private checkInterpanLock(): void { - if (this.interpanLock) { - console.error(`[INTERPAN MODE] Cannot execute non-InterPAN commands.`); - - // will be caught by request queue and rejected internally. - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); - } - } - -} diff --git a/src/adapter/ember/adapter/endpoints.ts b/src/adapter/ember/adapter/endpoints.ts deleted file mode 100644 index 4e9cc76bb7c..00000000000 --- a/src/adapter/ember/adapter/endpoints.ts +++ /dev/null @@ -1,80 +0,0 @@ -import Cluster from '../../../zcl/definition/cluster'; -import {GP_ENDPOINT, GP_PROFILE_ID, HA_PROFILE_ID} from '../consts'; -import {ClusterId, ProfileId} from '../types'; - - -type FixedEndpointInfo = { - /** Actual Zigbee endpoint number. uint8_t */ - endpoint: number, - /** Profile ID of the device on this endpoint. */ - profileId: ProfileId, - /** Device ID of the device on this endpoint. uint16_t*/ - deviceId: number, - /** Version of the device. uint8_t */ - deviceVersion: number, - /** List of server clusters. */ - inClusterList: ClusterId[], - /** List of client clusters. */ - outClusterList: ClusterId[], - /** Network index for this endpoint. uint8_t */ - networkIndex: number, -}; - - -/** - * List of endpoints to register. - * - * Index 0 is used as default and expected to be the primary network. - */ -export const FIXED_ENDPOINTS: readonly FixedEndpointInfo[] = [ - {// primary network - endpoint: 1, - profileId: HA_PROFILE_ID, - deviceId: 0x65,// ? - deviceVersion: 1, - inClusterList: [ - Cluster.genBasic.ID,// 0x0000,// Basic - Cluster.genIdentify.ID,// 0x0003,// Identify - Cluster.genOnOff.ID,// 0x0006,// On/off - Cluster.genLevelCtrl.ID,// 0x0008,// Level Control - Cluster.genTime.ID,// 0x000A,// Time - Cluster.genOta.ID,// 0x0019,// Over the Air Bootloading - // Cluster.genPowerProfile.ID,// 0x001A,// Power Profile XXX: missing ZCL cluster def in Z2M? - Cluster.lightingColorCtrl.ID,// 0x0300,// Color Control - ], - outClusterList: [ - Cluster.genBasic.ID,// 0x0000,// Basic - Cluster.genIdentify.ID,// 0x0003,// Identify - Cluster.genGroups.ID,// 0x0004,// Groups - Cluster.genScenes.ID,// 0x0005,// Scenes - Cluster.genOnOff.ID,// 0x0006,// On/off - Cluster.genLevelCtrl.ID,// 0x0008,// Level Control - Cluster.genPollCtrl.ID,// 0x0020,// Poll Control - Cluster.lightingColorCtrl.ID,// 0x0300,// Color Control - Cluster.msIlluminanceMeasurement.ID,// 0x0400,// Illuminance Measurement - Cluster.msTemperatureMeasurement.ID,// 0x0402,// Temperature Measurement - Cluster.msRelativeHumidity.ID,// 0x0405,// Relative Humidity Measurement - Cluster.msOccupancySensing.ID,// 0x0406,// Occupancy Sensing - Cluster.ssIasZone.ID,// 0x0500,// IAS Zone - Cluster.seMetering.ID,// 0x0702,// Simple Metering - Cluster.haMeterIdentification.ID,// 0x0B01,// Meter Identification - Cluster.haApplianceStatistics.ID,// 0x0B03,// Appliance Statistics - Cluster.haElectricalMeasurement.ID,// 0x0B04,// Electrical Measurement - Cluster.touchlink.ID,// 0x1000, // touchlink - ], - networkIndex: 0x00, - }, - {// green power - endpoint: GP_ENDPOINT, - profileId: GP_PROFILE_ID, - deviceId: 0x66, - deviceVersion: 1, - inClusterList: [ - Cluster.greenPower.ID,// 0x0021,// Green Power - ], - outClusterList: [ - Cluster.greenPower.ID,// 0x0021,// Green Power - ], - networkIndex: 0x00, - }, -]; diff --git a/src/adapter/ember/adapter/index.ts b/src/adapter/ember/adapter/index.ts deleted file mode 100644 index 581b5bed293..00000000000 --- a/src/adapter/ember/adapter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {EmberAdapter} from './emberAdapter'; - -export {EmberAdapter}; diff --git a/src/adapter/ember/adapter/oneWaitress.ts b/src/adapter/ember/adapter/oneWaitress.ts deleted file mode 100644 index f95f2d7ed56..00000000000 --- a/src/adapter/ember/adapter/oneWaitress.ts +++ /dev/null @@ -1,299 +0,0 @@ -/* istanbul ignore file */ -import equals from 'fast-deep-equal/es6'; -import {ZclDataPayload} from "../../events"; -import {TOUCHLINK_PROFILE_ID} from "../consts"; -import {EmberApsFrame, EmberNodeId} from "../types"; -import {EmberZdoStatus} from "../zdo"; - - -type OneWaitressMatcher = { - /** - * Matches `indexOrDestination` in `ezspMessageSentHandler` or `sender` in `ezspIncomingMessageHandler` - * Except for InterPAN touchlink, it should always be present. - */ - target?: EmberNodeId, - apsFrame: EmberApsFrame, - /** Cluster ID for when the response doesn't match the request. Takes priority over apsFrame.clusterId. */ - responseClusterId?: number, - zclSequence?: number; -}; - -type OneWaitressEventMatcher = { - eventName: string, - /** If supplied, keys/values are expected to match with resolve payload. */ - payload?: {[k: string]: unknown}, -}; - -interface Waiter { - id: number; - matcher: A; - timer?: NodeJS.Timeout; - resolve: (payload: B) => void; - reject: (error: Error) => void; - resolved: boolean; - timedout: boolean; -}; - -/** - * The one waitress to rule them all. Hopefully. - * Careful, she'll burn you if you're late on delivery! - * - * NOTE: `messageTag` is unreliable, so not used... - */ -export class EmberOneWaitress { - private waiters: Map>; - // NOTE: for now, this could be much simpler (array-like), but more complex events might come into play - private eventWaiters: Map>; - private currentId: number; - private currentEventId: number; - - public constructor() { - this.waiters = new Map(); - this.eventWaiters = new Map(); - this.currentId = 0; - this.currentEventId = 0; - } - - /** - * Reject because of failed delivery notified by `ezspMessageSentHandler`. - * NOTE: This checks for APS sequence, which is only valid in `ezspMessageSentHandler`, not `ezspIncomingMessageHandler` (sequence from stack) - * - * @param target - * @param apsFrame - * @returns - */ - public deliveryFailedFor(target: number, apsFrame: EmberApsFrame): boolean { - for (const [index, waiter] of this.waiters.entries()) { - if (waiter.timedout) { - this.waiters.delete(index); - continue; - } - - // no target in touchlink - // in `ezspMessageSentHandler`, the clusterId for ZDO is still the request one, so check against apsFrame, not override - if (((waiter.matcher.apsFrame.profileId === TOUCHLINK_PROFILE_ID) || (target === waiter.matcher.target)) - && (apsFrame.sequence === waiter.matcher.apsFrame.sequence) && (apsFrame.profileId === waiter.matcher.apsFrame.profileId) - && (apsFrame.clusterId === waiter.matcher.apsFrame.clusterId)) { - clearTimeout(waiter.timer); - - waiter.resolved = true; - - this.waiters.delete(index); - waiter.reject(new Error(`Delivery failed for ${JSON.stringify(apsFrame)}`)); - - return true; - } - } - - return false; - } - - /** - * Resolve or reject ZDO response based on given status. - * @param status - * @param sender - * @param apsFrame - * @param payload - * @returns - */ - public resolveZDO(status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: unknown): boolean { - for (const [index, waiter] of this.waiters.entries()) { - if (waiter.timedout) { - this.waiters.delete(index); - continue; - } - - // always a sender expected in ZDO, profileId is a bit redundant here, but... - if ((sender === waiter.matcher.target) && (apsFrame.profileId === waiter.matcher.apsFrame.profileId) - && (apsFrame.clusterId === (waiter.matcher.responseClusterId != null ? - waiter.matcher.responseClusterId : waiter.matcher.apsFrame.clusterId))) { - clearTimeout(waiter.timer); - - waiter.resolved = true; - - 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. - console.log(`[ZDO] Received status ZDP_NO_ENTRY for "${sender}" cluster "${apsFrame.clusterId}". Ignoring.`); - waiter.resolve(payload); - } else { - waiter.reject(new Error(`[ZDO] Failed response by NCP for "${sender}" cluster "${apsFrame.clusterId}" ` - + `with status=${EmberZdoStatus[status]}.`)); - } - - return true; - } - } - - return false; - } - - public resolveZCL(payload: ZclDataPayload): boolean { - for (const [index, waiter] of this.waiters.entries()) { - if (waiter.timedout) { - this.waiters.delete(index); - continue; - } - - // no target in touchlink, also no APS sequence, but use the ZCL one instead - if (((waiter.matcher.apsFrame.profileId === TOUCHLINK_PROFILE_ID) || (payload.address === waiter.matcher.target)) - && (!waiter.matcher.zclSequence || (payload.frame.Header.transactionSequenceNumber === waiter.matcher.zclSequence)) - && (payload.frame.Cluster.ID === waiter.matcher.apsFrame.clusterId) - && (payload.endpoint === waiter.matcher.apsFrame.destinationEndpoint)) { - clearTimeout(waiter.timer); - - waiter.resolved = true; - - this.waiters.delete(index); - waiter.resolve(payload); - - return true; - } - } - - return false; - } - - public waitFor(matcher: OneWaitressMatcher, timeout: number): {id: number; start: () => {promise: Promise; id: number}} { - const id = this.currentId++; - this.currentId &= 0xFFFF;// roll-over every so often - 65535 should be enough not to create conflicts ;-) - - const promise: Promise = new Promise((resolve, reject): void => { - const object: Waiter = {matcher, resolve, reject, timedout: false, resolved: false, id}; - - this.waiters.set(id, object); - }); - - const start = (): {promise: Promise; id: number} => { - const waiter = this.waiters.get(id); - - if (waiter && !waiter.resolved && !waiter.timer) { - // Capture the stack trace from the caller of start() - const error = new Error(); - Error.captureStackTrace(error); - - waiter.timer = setTimeout((): void => { - error.message = `${JSON.stringify(matcher)} timed out after ${timeout}ms`; - waiter.timedout = true; - - waiter.reject(error); - }, timeout); - } - - return {promise, id}; - }; - - return {id, start}; - } - - /** - * Shortcut that starts the timer immediately and returns the promise. - * No access to `id`, so no easy cancel. - * @param matcher - * @param timeout - * @returns - */ - public startWaitingFor(matcher: OneWaitressMatcher, timeout: number): Promise { - return this.waitFor(matcher, timeout).start().promise; - } - - public remove(id: number): void { - const waiter = this.waiters.get(id); - - if (waiter) { - if (!waiter.timedout && waiter.timer) { - clearTimeout(waiter.timer); - } - - this.waiters.delete(id); - } - } - - /** - * Matches event name with matcher's, and payload (if any in matcher) using `fast-deep-equal/es6` (all keys & values must match) - * @param eventName - * @param payload - * @returns - */ - public resolveEvent(eventName: string, payload?: {[k: string]: unknown}): boolean { - for (const [index, waiter] of this.eventWaiters.entries()) { - if (waiter.timedout) { - this.eventWaiters.delete(index); - continue; - } - - if (eventName === waiter.matcher.eventName && (!waiter.matcher.payload || (equals(payload, waiter.matcher.payload)))) { - clearTimeout(waiter.timer); - - waiter.resolved = true; - - this.eventWaiters.delete(index); - waiter.resolve(payload); - - return true; - } - } - } - - public waitForEvent(matcher: OneWaitressEventMatcher, timeout: number, reason: string = null) - : {id: number; start: () => {promise: Promise; id: number}} { - // NOTE: logic is very much the same as `waitFor`, just different matcher - const id = this.currentEventId++; - this.currentEventId &= 0xFFFF;// roll-over every so often - 65535 should be enough not to create conflicts ;-) - - const promise: Promise = new Promise((resolve, reject): void => { - const object: Waiter = {matcher, resolve, reject, timedout: false, resolved: false, id}; - - this.eventWaiters.set(id, object); - }); - - const start = (): {promise: Promise; id: number} => { - const waiter = this.eventWaiters.get(id); - - if (waiter && !waiter.resolved && !waiter.timer) { - // Capture the stack trace from the caller of start() - const error = new Error(); - Error.captureStackTrace(error); - - waiter.timer = setTimeout((): void => { - error.message = `${reason ? reason : JSON.stringify(matcher)} timed out after ${timeout}ms`; - waiter.timedout = true; - - waiter.reject(error); - }, timeout); - } - - return {promise, id}; - }; - - return {id, start}; - } - - /** - * Shortcut that starts the timer immediately and returns the promise. - * No access to `id`, so no easy cancel. - * @param matcher - * @param timeout - * @param reason If supplied, will be used as timeout label, otherwise stringified matcher is. - * @returns - */ - public startWaitingForEvent(matcher: OneWaitressEventMatcher, timeout: number, reason: string = null): Promise { - return this.waitForEvent(matcher, timeout, reason).start().promise; - } - - public removeEvent(id: number): void { - const waiter = this.eventWaiters.get(id); - - if (waiter) { - if (!waiter.timedout && waiter.timer) { - clearTimeout(waiter.timer); - } - - this.eventWaiters.delete(id); - } - } -} diff --git a/src/adapter/ember/adapter/requestQueue.ts b/src/adapter/ember/adapter/requestQueue.ts deleted file mode 100644 index 860f1430a68..00000000000 --- a/src/adapter/ember/adapter/requestQueue.ts +++ /dev/null @@ -1,160 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import {EmberStatus, EzspStatus} from "../enums"; - -const debug = Debug('zigbee-herdsman:adapter: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. - */ - sendAttempts: number; - /** The function the entry is supposed to execute. */ - func: () => Promise; - /** The wrapping promise's reject to reject if necessary. */ - reject: (reason: Error) => void; -}; - -export const NETWORK_BUSY_DEFER_MSEC = 500; -export const NETWORK_DOWN_DEFER_MSEC = 1500; - -export class EmberRequestQueue { - private readonly dispatchInterval: number; - /** Interval handler that manages `dispatch()` */ - private dispatchHandler: NodeJS.Timeout; - /** If true, the queue is currently busy dispatching. */ - private dispatching: boolean; - /** The queue holding requests to be sent. */ - private queue: EmberRequestQueueEntry[]; - /** Queue with requests that should take priority over the above queue. */ - private priorityQueue: EmberRequestQueueEntry[]; - - constructor(dispatchInterval: number) { - this.dispatchInterval = dispatchInterval || 60; - this.dispatching = false; - this.queue = []; - this.priorityQueue = []; - } - - /** - * Empty each queue. - */ - public clear(): void { - this.queue = []; - this.priorityQueue = []; - } - - /** - * Prevent sending requests (usually due to NCP being reset). - */ - public stopDispatching(): void { - clearInterval(this.dispatchHandler); - - debug(`Dispatching stopped; queue=${this.queue.length} priorityQueue=${this.priorityQueue.length}`); - } - - /** - * Allow sending requests. - * Must be called after init. - */ - public startDispatching(): void { - this.dispatchHandler = setInterval(this.dispatch.bind(this), this.dispatchInterval); - - debug(`Dispatching started.`); - } - - /** - * Store a function in the queue to be resolved when appropriate. - * @param function The function to enqueue. Upon dispatch: - * - if its return value is one of MAX_MESSAGE_LIMIT_REACHED, NETWORK_BUSY, NETWORK_DOWN, - * queue will defer dispatching and keep the function in the queue; reject otherwise. - * - if it throws, it is expected to throw `EzspStatus`, and will act same as above if one of NOT_CONNECTED, NO_TX_SPACE; reject otherwise. - * - any other value will result in the function being removed from the queue. - * @param reject The `reject` of the Promise wrapping the `enqueue` call - * (`resolve` is done in `func` directly to have typing on results & control on exec). - * @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 { - debug(`Status queue=${this.queue.length} priorityQueue=${this.priorityQueue.length}.`); - return (prioritize ? this.priorityQueue : this.queue).push({ - sendAttempts: 0, - func, - reject, - }); - } - - /** - * 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` resolves but has an error, look at what error, and determine if should retry or remove the request from queue. - * - * If request `func` resolves without error, remove request from queue. - * - * WARNING: Because of this logic for "internal retries", any error thrown by `func` will not immediatedly bubble back to Adapter/Controller - */ - public async dispatch(): Promise { - if (this.dispatching) { - return; - } - - let fromPriorityQueue = true; - let entry = this.priorityQueue[0];// head of queue if any, priority first - - if (!entry) { - fromPriorityQueue = false; - entry = this.queue[0]; - } - - if (entry) { - this.dispatching = true; - entry.sendAttempts++; - - // NOTE: refer to `enqueue()` comment to keep logic in sync with expectations, adjust comment on change. - try { - const status: EmberStatus = (await entry.func()); - - if ((status === EmberStatus.MAX_MESSAGE_LIMIT_REACHED) || (status === EmberStatus.NETWORK_BUSY)) { - debug(`Dispatching deferred: NCP busy.`); - this.defer(NETWORK_BUSY_DEFER_MSEC); - } else if (status === EmberStatus.NETWORK_DOWN) { - debug(`Dispatching deferred: Network not ready`); - this.defer(NETWORK_DOWN_DEFER_MSEC); - } else { - // success - (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); - - if (status !== EmberStatus.SUCCESS) { - entry.reject(new Error(EmberStatus[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]) { - debug(`Dispatching deferred: Host busy.`); - this.defer(NETWORK_BUSY_DEFER_MSEC); - } else if (err.message === EzspStatus[EzspStatus.NOT_CONNECTED]) { - debug(`Dispatching deferred: Network not ready`); - this.defer(NETWORK_DOWN_DEFER_MSEC); - } else { - (fromPriorityQueue ? this.priorityQueue : this.queue).shift(); - entry.reject(err); - } - } finally { - this.dispatching = false; - } - } - } - - /** - * Defer dispatching for the specified duration (in msec). - * @param msec - */ - public defer(msec: number): void { - this.stopDispatching(); - - setTimeout(this.startDispatching.bind(this), msec); - } -} diff --git a/src/adapter/ember/adapter/tokensManager.ts b/src/adapter/ember/adapter/tokensManager.ts deleted file mode 100644 index ed6fbe6ba50..00000000000 --- a/src/adapter/ember/adapter/tokensManager.ts +++ /dev/null @@ -1,780 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import {initSecurityManagerContext} from "../utils/initters"; -import {BLANK_EUI64} from "../consts"; -import {EMBER_ENCRYPTION_KEY_SIZE, EUI64_SIZE} from "../ezsp/consts"; -import {EmberStatus, EzspStatus, SLStatus, SecManFlag, SecManKeyType} from "../enums"; -import {EzspValueId} from "../ezsp/enums"; -import {EmberTokenData, SecManKey} from "../types"; -import {Ezsp} from "../ezsp/ezsp"; - -const debug = Debug('zigbee-herdsman:adapter:ember:adapter:tokens'); - -/* eslint-disable @typescript-eslint/no-unused-vars */ -//------------------------------------------------------------------------------ -// Definitions for stack tokens. -// protocol\zigbee\stack\config\token-stack.h - -/** - * Creator Codes - * - * The CREATOR is used as a distinct identifier tag for the token. - * - * The CREATOR is necessary because the token name is defined differently depending on the hardware platform. - * Therefore, the CREATOR ensures that token definitions and data stay tagged and known. - * The only requirement is that each creator definition must be unique. - * See hal/micro/token.h for a more complete explanation. - * - */ -// STACK CREATORS -const CREATOR_STACK_NVDATA_VERSION = 0xFF01; -const CREATOR_STACK_BOOT_COUNTER = 0xE263; -const CREATOR_STACK_NONCE_COUNTER = 0xE563; -const CREATOR_STACK_ANALYSIS_REBOOT = 0xE162; -const CREATOR_STACK_KEYS = 0xEB79; -const CREATOR_STACK_NODE_DATA = 0xEE64; -const CREATOR_STACK_CLASSIC_DATA = 0xE364; -const CREATOR_STACK_ALTERNATE_KEY = 0xE475; -const CREATOR_STACK_APS_FRAME_COUNTER = 0xE123; -const CREATOR_STACK_TRUST_CENTER = 0xE124; -const CREATOR_STACK_NETWORK_MANAGEMENT = 0xE125; -const CREATOR_STACK_PARENT_INFO = 0xE126; -const CREATOR_STACK_PARENT_ADDITIONAL_INFO = 0xE127; -const CREATOR_STACK_MULTI_PHY_NWK_INFO = 0xE128; -const CREATOR_STACK_MIN_RECEIVED_RSSI = 0xE129; -// Restored EUI64 -const CREATOR_STACK_RESTORED_EUI64 = 0xE12A; - -// MULTI-NETWORK STACK CREATORS -const CREATOR_MULTI_NETWORK_STACK_KEYS = 0xE210; -const CREATOR_MULTI_NETWORK_STACK_NODE_DATA = 0xE211; -const CREATOR_MULTI_NETWORK_STACK_ALTERNATE_KEY = 0xE212; -const CREATOR_MULTI_NETWORK_STACK_TRUST_CENTER = 0xE213; -const CREATOR_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT = 0xE214; -const CREATOR_MULTI_NETWORK_STACK_PARENT_INFO = 0xE215; - -// A temporary solution for multi-network nwk counters: -// This counter will be used on the network with index 1. -const CREATOR_MULTI_NETWORK_STACK_NONCE_COUNTER = 0xE220; -const CREATOR_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO = 0xE221; - -// GP stack tokens. -const CREATOR_STACK_GP_DATA = 0xE258; -const CREATOR_STACK_GP_PROXY_TABLE = 0xE259; -const CREATOR_STACK_GP_SINK_TABLE = 0xE25A; -const CREATOR_STACK_GP_INCOMING_FC = 0xE25B; -const CREATOR_STACK_GP_INCOMING_FC_IN_SINK = 0xE25C; -// APP CREATORS -const CREATOR_STACK_BINDING_TABLE = 0xE274; -const CREATOR_STACK_CHILD_TABLE = 0xFF0D; -const CREATOR_STACK_KEY_TABLE = 0xE456; -const CREATOR_STACK_CERTIFICATE_TABLE = 0xE500; -const CREATOR_STACK_ZLL_DATA = 0xE501; -const CREATOR_STACK_ZLL_SECURITY = 0xE502; -const CREATOR_STACK_ADDITIONAL_CHILD_DATA = 0xE503; - - -/** - * NVM3 Object Keys - * - * The NVM3 object key is used as a distinct identifier tag for a token stored in NVM3. - * - * Every token must have a defined NVM3 object key and the object key must be unique. - * The object key defined must be in the following format: - * - * NVM3KEY_tokenname where tokenname is the name of the token without NVM3KEY_ or TOKEN_ prefix. - * - */ -// NVM3KEY domain base keys -const NVM3KEY_DOMAIN_USER = 0x00000; -const NVM3KEY_DOMAIN_ZIGBEE = 0x10000; -const NVM3KEY_DOMAIN_COMMON = 0x80000; - -// STACK KEYS -const NVM3KEY_STACK_NVDATA_VERSION = (NVM3KEY_DOMAIN_ZIGBEE | 0xFF01); -const NVM3KEY_STACK_BOOT_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE263); -const NVM3KEY_STACK_NONCE_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE563); -const NVM3KEY_STACK_ANALYSIS_REBOOT = (NVM3KEY_DOMAIN_ZIGBEE | 0xE162); -const NVM3KEY_STACK_KEYS = (NVM3KEY_DOMAIN_ZIGBEE | 0xEB79); -const NVM3KEY_STACK_NODE_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xEE64); -const NVM3KEY_STACK_CLASSIC_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE364); -const NVM3KEY_STACK_ALTERNATE_KEY = (NVM3KEY_DOMAIN_ZIGBEE | 0xE475); -const NVM3KEY_STACK_APS_FRAME_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE123); -const NVM3KEY_STACK_TRUST_CENTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE124); -const NVM3KEY_STACK_NETWORK_MANAGEMENT = (NVM3KEY_DOMAIN_ZIGBEE | 0xE125); -const NVM3KEY_STACK_PARENT_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE126); -const NVM3KEY_STACK_PARENT_ADDITIONAL_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE127); -const NVM3KEY_STACK_MULTI_PHY_NWK_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0xE128); -const NVM3KEY_STACK_MIN_RECEIVED_RSSI = (NVM3KEY_DOMAIN_ZIGBEE | 0xE129); -// Restored EUI64 -const NVM3KEY_STACK_RESTORED_EUI64 = (NVM3KEY_DOMAIN_ZIGBEE | 0xE12A); - -// MULTI-NETWORK STACK KEYS -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_KEYS = (NVM3KEY_DOMAIN_ZIGBEE | 0x0000); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0x0080); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY = (NVM3KEY_DOMAIN_ZIGBEE | 0x0100); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER = (NVM3KEY_DOMAIN_ZIGBEE | 0x0180); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT = (NVM3KEY_DOMAIN_ZIGBEE | 0x0200); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0x0280); - -// Temporary solution for multi-network nwk counters: -// This counter will be used on the network with index 1. -const NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER = (NVM3KEY_DOMAIN_ZIGBEE | 0xE220); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved -const NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO = (NVM3KEY_DOMAIN_ZIGBEE | 0x0300); - -// GP stack tokens. -const NVM3KEY_STACK_GP_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE258); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_GP_PROXY_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0380); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_GP_SINK_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0400); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved -const NVM3KEY_STACK_GP_INCOMING_FC = (NVM3KEY_DOMAIN_ZIGBEE | 0x0480); - -// APP KEYS -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_BINDING_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0500); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_CHILD_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0580); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_KEY_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0600); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_CERTIFICATE_TABLE = (NVM3KEY_DOMAIN_ZIGBEE | 0x0680); -const NVM3KEY_STACK_ZLL_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0xE501); -const NVM3KEY_STACK_ZLL_SECURITY = (NVM3KEY_DOMAIN_ZIGBEE | 0xE502); -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved. -const NVM3KEY_STACK_ADDITIONAL_CHILD_DATA = (NVM3KEY_DOMAIN_ZIGBEE | 0x0700); - -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved -const NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK = (NVM3KEY_DOMAIN_ZIGBEE | 0x0780); - -// XXX: comment out in prod, along with debug token prints -// const DEBUG_TOKEN_STRINGS = { -// [NVM3KEY_STACK_NVDATA_VERSION]: 'NVM3KEY_STACK_NVDATA_VERSION', -// [NVM3KEY_STACK_BOOT_COUNTER]: 'NVM3KEY_STACK_BOOT_COUNTER', -// [NVM3KEY_STACK_NONCE_COUNTER]: 'NVM3KEY_STACK_NONCE_COUNTER', -// [NVM3KEY_STACK_ANALYSIS_REBOOT]: 'NVM3KEY_STACK_ANALYSIS_REBOOT', -// [NVM3KEY_STACK_KEYS]: 'NVM3KEY_STACK_KEYS', -// [NVM3KEY_STACK_NODE_DATA]: 'NVM3KEY_STACK_NODE_DATA', -// [NVM3KEY_STACK_CLASSIC_DATA]: 'NVM3KEY_STACK_CLASSIC_DATA', -// [NVM3KEY_STACK_ALTERNATE_KEY]: 'NVM3KEY_STACK_ALTERNATE_KEY', -// [NVM3KEY_STACK_APS_FRAME_COUNTER]: 'NVM3KEY_STACK_APS_FRAME_COUNTER', -// [NVM3KEY_STACK_TRUST_CENTER]: 'NVM3KEY_STACK_TRUST_CENTER', -// [NVM3KEY_STACK_NETWORK_MANAGEMENT]: 'NVM3KEY_STACK_NETWORK_MANAGEMENT', -// [NVM3KEY_STACK_PARENT_INFO]: 'NVM3KEY_STACK_PARENT_INFO', -// [NVM3KEY_STACK_PARENT_ADDITIONAL_INFO]: 'NVM3KEY_STACK_PARENT_ADDITIONAL_INFO', -// [NVM3KEY_STACK_MULTI_PHY_NWK_INFO]: 'NVM3KEY_STACK_MULTI_PHY_NWK_INFO', -// [NVM3KEY_STACK_MIN_RECEIVED_RSSI]: 'NVM3KEY_STACK_MIN_RECEIVED_RSSI', -// [NVM3KEY_STACK_RESTORED_EUI64]: 'NVM3KEY_STACK_RESTORED_EUI64', -// [NVM3KEY_MULTI_NETWORK_STACK_KEYS]: 'NVM3KEY_MULTI_NETWORK_STACK_KEYS', -// [NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA]: 'NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA', -// [NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY]: 'NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY', -// [NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER]: 'NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER', -// [NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT]: 'NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT', -// [NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO]: 'NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO', -// [NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER]: 'NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER', -// [NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO]: 'NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO', -// [NVM3KEY_STACK_GP_DATA]: 'NVM3KEY_STACK_GP_DATA', -// [NVM3KEY_STACK_GP_PROXY_TABLE]: 'NVM3KEY_STACK_GP_PROXY_TABLE', -// [NVM3KEY_STACK_GP_SINK_TABLE]: 'NVM3KEY_STACK_GP_SINK_TABLE', -// [NVM3KEY_STACK_GP_INCOMING_FC]: 'NVM3KEY_STACK_GP_INCOMING_FC', -// [NVM3KEY_STACK_BINDING_TABLE]: 'NVM3KEY_STACK_BINDING_TABLE', -// [NVM3KEY_STACK_CHILD_TABLE]: 'NVM3KEY_STACK_CHILD_TABLE', -// [NVM3KEY_STACK_KEY_TABLE]: 'NVM3KEY_STACK_KEY_TABLE', -// [NVM3KEY_STACK_CERTIFICATE_TABLE]: 'NVM3KEY_STACK_CERTIFICATE_TABLE', -// [NVM3KEY_STACK_ZLL_DATA]: 'NVM3KEY_STACK_ZLL_DATA', -// [NVM3KEY_STACK_ZLL_SECURITY]: 'NVM3KEY_STACK_ZLL_SECURITY', -// [NVM3KEY_STACK_ADDITIONAL_CHILD_DATA]: 'NVM3KEY_STACK_ADDITIONAL_CHILD_DATA', -// [NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK]: 'NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK', -// }; - -/** - * The current version number of the stack tokens. - * MSB is the version, LSB is a complement. - * - * See hal/micro/token.h for a more complete explanation. - */ -const CURRENT_STACK_TOKEN_VERSION = 0x03FC; - -/** 8-byte IEEE + 16-byte Key + 1-byte info */ -const KEY_TABLE_ENTRY_SIZE = 25; -const KEY_ENTRY_IEEE_OFFSET = 0; -/** first 4 bytes may point to PSA ID if data[KEY_ENTRY_INFO_OFFSET] & KEY_TABLE_ENTRY_HAS_PSA_ID */ -const KEY_ENTRY_KEY_DATA_OFFSET = 8; -const KEY_ENTRY_INFO_OFFSET = 24; -/* eslint-enable @typescript-eslint/no-unused-vars */ - -/** uint16_t */ -const CREATORS: number[] = [ - CREATOR_STACK_NVDATA_VERSION, - CREATOR_STACK_BOOT_COUNTER, - CREATOR_STACK_NONCE_COUNTER, - CREATOR_STACK_ANALYSIS_REBOOT, - CREATOR_STACK_KEYS, - CREATOR_STACK_NODE_DATA, - CREATOR_STACK_CLASSIC_DATA, - CREATOR_STACK_ALTERNATE_KEY, - CREATOR_STACK_APS_FRAME_COUNTER, - CREATOR_STACK_TRUST_CENTER, - CREATOR_STACK_NETWORK_MANAGEMENT, - CREATOR_STACK_PARENT_INFO, - CREATOR_STACK_PARENT_ADDITIONAL_INFO, - CREATOR_STACK_MULTI_PHY_NWK_INFO, - CREATOR_STACK_MIN_RECEIVED_RSSI, - CREATOR_STACK_RESTORED_EUI64, - CREATOR_MULTI_NETWORK_STACK_KEYS, - CREATOR_MULTI_NETWORK_STACK_NODE_DATA, - CREATOR_MULTI_NETWORK_STACK_ALTERNATE_KEY, - CREATOR_MULTI_NETWORK_STACK_TRUST_CENTER, - CREATOR_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT, - CREATOR_MULTI_NETWORK_STACK_PARENT_INFO, - CREATOR_MULTI_NETWORK_STACK_NONCE_COUNTER, - CREATOR_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO, - CREATOR_STACK_GP_DATA, - CREATOR_STACK_GP_PROXY_TABLE, - CREATOR_STACK_GP_SINK_TABLE, - CREATOR_STACK_GP_INCOMING_FC, - CREATOR_STACK_GP_INCOMING_FC_IN_SINK, - CREATOR_STACK_BINDING_TABLE, - CREATOR_STACK_CHILD_TABLE, - CREATOR_STACK_KEY_TABLE, - CREATOR_STACK_CERTIFICATE_TABLE, - CREATOR_STACK_ZLL_DATA, - CREATOR_STACK_ZLL_SECURITY, - CREATOR_STACK_ADDITIONAL_CHILD_DATA, -]; - -/** uint32_t */ -const NVM3KEYS: number[] = [ - NVM3KEY_STACK_NVDATA_VERSION, - NVM3KEY_STACK_BOOT_COUNTER, - NVM3KEY_STACK_NONCE_COUNTER, - NVM3KEY_STACK_ANALYSIS_REBOOT, - NVM3KEY_STACK_KEYS, - NVM3KEY_STACK_NODE_DATA, - NVM3KEY_STACK_CLASSIC_DATA, - NVM3KEY_STACK_ALTERNATE_KEY, - NVM3KEY_STACK_APS_FRAME_COUNTER, - NVM3KEY_STACK_TRUST_CENTER, - NVM3KEY_STACK_NETWORK_MANAGEMENT, - NVM3KEY_STACK_PARENT_INFO, - NVM3KEY_STACK_PARENT_ADDITIONAL_INFO, - NVM3KEY_STACK_MULTI_PHY_NWK_INFO, - NVM3KEY_STACK_MIN_RECEIVED_RSSI, - NVM3KEY_STACK_RESTORED_EUI64, - NVM3KEY_MULTI_NETWORK_STACK_KEYS, - NVM3KEY_MULTI_NETWORK_STACK_NODE_DATA, - NVM3KEY_MULTI_NETWORK_STACK_ALTERNATE_KEY, - NVM3KEY_MULTI_NETWORK_STACK_TRUST_CENTER, - NVM3KEY_MULTI_NETWORK_STACK_NETWORK_MANAGEMENT, - NVM3KEY_MULTI_NETWORK_STACK_PARENT_INFO, - NVM3KEY_MULTI_NETWORK_STACK_NONCE_COUNTER, - NVM3KEY_MULTI_NETWORK_STACK_PARENT_ADDITIONAL_INFO, - NVM3KEY_STACK_GP_DATA, - NVM3KEY_STACK_GP_PROXY_TABLE, - NVM3KEY_STACK_GP_SINK_TABLE, - NVM3KEY_STACK_GP_INCOMING_FC, - NVM3KEY_STACK_BINDING_TABLE, - NVM3KEY_STACK_CHILD_TABLE, - NVM3KEY_STACK_KEY_TABLE, - NVM3KEY_STACK_CERTIFICATE_TABLE, - NVM3KEY_STACK_ZLL_DATA, - NVM3KEY_STACK_ZLL_SECURITY, - NVM3KEY_STACK_ADDITIONAL_CHILD_DATA, - NVM3KEY_STACK_GP_INCOMING_FC_IN_SINK, -]; - -const BLANK_EUI64_BUF = Buffer.from(BLANK_EUI64.substring(2)/*take out 0x*/, 'hex'); - -export class EmberTokensManager { - - /** - * Host-only API to check whether the NCP uses key storage. - * - * @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)); - - if ((status !== EzspStatus.SUCCESS) || (valueLength < 1)) { - throw new Error(`[TOKENS] Error retrieving key storage version, status=${EzspStatus[status]}.`); - } - - return (value[0] === 1); - } - - /** - * Matcher for Zigbeed tokens. - * @param nvm3Key - * @returns - */ - private static getCreatorFromNvm3Key(nvm3Key: number): number { - for (let i = 0; i < NVM3KEYS.length; i++) { - if (NVM3KEYS[i] === nvm3Key) { - return CREATORS[i]; - } - } - - return 0xFFFF; - } - - /** - * Saves tokens. Only for NVM3-based NCP. - * - * The binary file format to save the tokens are - * - * Number of Tokens (1 byte) - * Token0 (4 bytes) Token0Size(1 byte) Token0ArraySize(1 byte) Token0Data(Token0Size * Token0ArraySize) - * : - * : - * TokenM (4 bytes) TokenMSize(1 byte) TokenMArraySize(1 byte) TokenMData(TokenMSize * TokenMArraySize) - * - * @param localEui64 Used in place of blank `restoredEui64` keys - * - * @return Saved tokens buffer or null. - */ - public static async saveTokens(ezsp: Ezsp, localEui64: Buffer): Promise { - console.log(`[TOKENS] Saving tokens...`); - const tokenCount = (await ezsp.ezspGetTokenCount()); - - if (tokenCount) { - 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)); - - debug(`[TOKENS] Saving ${tokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`); - - for (let i = 0; i < tokenCount; i++) { - const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); - let writeOffset: number = 0; - - if (tiStatus === EmberStatus.SUCCESS) { - const outputToken = Buffer.alloc(4 + 1 + 1 + (tokenInfo.size * tokenInfo.arraySize)); - outputToken.writeUInt32LE(tokenInfo.nvm3Key, writeOffset);// 4 bytes - writeOffset += 4; - outputToken.writeUInt8(tokenInfo.size, writeOffset++);// 1 byte - outputToken.writeUInt8(tokenInfo.arraySize, writeOffset++);// 1 byte - - for (let arrayIndex = 0; arrayIndex < tokenInfo.arraySize; arrayIndex++) { - const [tdStatus, tokenData] = (await ezsp.ezspGetTokenData(tokenInfo.nvm3Key, arrayIndex)); - - if (tdStatus === EmberStatus.SUCCESS) { - if (hasSecureStorage) { - // Populate keys into tokenData because tokens do not contain them with secure key storage - await EmberTokensManager.saveKeysToData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex); - - // ensure the token data was retrieved properly, length should match the size announced by the token info - console.assert( - tokenData.data.length === tokenInfo.size, - `[TOKENS] Mismatch in token data size; got ${tokenData.data.length}, expected ${tokenInfo.size}.` - ); - } - // debug(`[TOKENS] TOKEN nvm3Key=${DEBUG_TOKEN_STRINGS[tokenInfo.nvm3Key]} size=${tokenInfo.size} ` - // + `arraySize=${tokenInfo.arraySize} token=${tokenData.data.toString('hex')}`); - - // Check the Key to see if the token to save is restoredEui64, in that case - // check if it is blank, then save the node EUI64 in its place, else save the value - // 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)) { - // Special case : Save the node EUI64 on the restoredEui64 token while saving. - tokenData.data.set(localEui64); - debug(`[TOKENS] Saved node EUI64 in place of blank RESTORED EUI64.`); - } - - outputToken.set(tokenData.data, writeOffset); - writeOffset += tokenData.size; - } else { - console.error(`[TOKENS] Failed to get token data at index ${arrayIndex} with status=${EmberStatus[tdStatus]}.`); - } - } - - chunks.push(outputToken); - } else { - console.error(`[TOKENS] Failed to get token info at index ${i} with status=${EmberStatus[tiStatus]}.`); - } - } - - return Buffer.concat(chunks); - } else { - // ezspGetTokenCount == 0 OR (ezspGetTokenInfo|ezspGetTokenData|ezspSetTokenData return LIBRARY_NOT_PRESENT) - // ezspTokenFactoryReset will do nothing. - console.error(`[TOKENS] Saving tokens not supported by NCP (not NVM3-based).`); - } - - return null; - } - - /** - * Restores tokens. Only for NVM3-based NCP. - * XXX: If a previous backup from an NVM3 NCP is attempted on a non-NVM3 NCP, - * it should just fail (LIBRARY_NOT_PRESENT all on token-related functions). - * - * @see EmberTokensManager.saveTokens() for format - * - * @return EmberStatus status code - */ - public static async restoreTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { - if (!inBuffer?.length) { - throw new Error(`[TOKENS] Restore tokens buffer empty.`); - } - - console.log(`[TOKENS] Restoring tokens...`); - - let readOffset: number = 0; - const inTokenCount = inBuffer.readUInt8(readOffset++); - const hasSecureStorage: boolean = (await EmberTokensManager.ncpUsesPSAKeyStorage(ezsp)); - - debug(`[TOKENS] Restoring ${inTokenCount} tokens, ${hasSecureStorage ? "with" : "without"} secure storage.`); - - for (let i = 0; i < inTokenCount; i++) { - const [tiStatus, tokenInfo] = (await ezsp.ezspGetTokenInfo(i)); - - if (tiStatus === EmberStatus.SUCCESS) { - const nvm3Key = inBuffer.readUInt32LE(readOffset);// 4 bytes Token Key/Creator - readOffset += 4; - const size = inBuffer.readUInt8(readOffset++);// 1 byte token size - const arraySize = inBuffer.readUInt8(readOffset++);// 1 byte array size. - - for (let arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { - const tokenData: EmberTokenData = { - data: inBuffer.subarray(readOffset, readOffset + size), - size, - }; - - if (hasSecureStorage) { - // do not keep keys in classic key storage upon restoration - await EmberTokensManager.restoreKeysFromData(ezsp, tokenData, tokenInfo.nvm3Key, arrayIndex); - } - - const status = (await ezsp.ezspSetTokenData(nvm3Key, arrayIndex, tokenData)) as EmberStatus; - - console.assert( - status === EmberStatus.SUCCESS, - `[TOKENS] Failed to set token data for key "${nvm3Key}" with status=${EmberStatus[status]}.` - ); - - readOffset += tokenData.size; - } - } else { - console.error(`[TOKENS] Failed to get token info at index ${i} with status=${EmberStatus[tiStatus]}.`); - } - } - - return EmberStatus.SUCCESS; - } - - /** - * Secure key storage needs to export the keys first so backup file has them. - * - * @param tokenData EmberTokenData* [IN/OUT] - * @param nvm3Key uint32_t - * @param index uint8_t - * @returns - */ - private static async saveKeysToData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { - let status: SLStatus = SLStatus.OK; - const context = initSecurityManagerContext(); - let plaintextKey: SecManKey; - - if (nvm3Key === NVM3KEY_STACK_KEYS) { - // typedef struct { - // uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t activeKeySeqNum; - // } tokTypeStackKeys; - - context.coreKeyType = SecManKeyType.NETWORK; - context.keyIndex = 0; - - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); - - tokenData.data.set(plaintextKey.contents, 0);// at beginning - } else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) { - // typedef struct { - // uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t activeKeySeqNum; - // } tokTypeStackKeys; - - context.coreKeyType = SecManKeyType.NETWORK; - context.keyIndex = 1; - - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); - - tokenData.data.set(plaintextKey.contents, 0);// at beginning - } else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) { - // typedef struct { - // uint16_t mode; - // uint8_t eui64[8]; - // uint8_t key[16]; // ignored if (mode & TRUST_CENTER_KEY_LIVES_IN_PSA) - // } tokTypeStackTrustCenter; - - context.coreKeyType = SecManKeyType.TC_LINK; - - [plaintextKey, status] = (await ezsp.ezspExportKey(context)); - - tokenData.data.set(plaintextKey.contents, 2 + EUI64_SIZE);// uint16_t+uint8_t[8] - } else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) { - // typedef uint8_t tokTypeStackKeyTable[25]; - - context.coreKeyType = SecManKeyType.APP_LINK; - context.keyIndex = index; - //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)); - - tokenData.data.set(plaintextKey.contents, KEY_ENTRY_KEY_DATA_OFFSET);// end part of uint8_t[25] - } else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) { - // typedef struct { - // uint8_t status; - // uint32_t options; - // //EmberGpAddress gpd; - // uint8_t gpAddress[8]; - // uint8_t endpoint; - // //uint16_t assignedAlias; - // uint8_t securityOptions; - // uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // //EmberGpSinkListEntry sinkList[2]; - // uint8_t sinkType[2]; - // uint8_t sinkEUI[2][8]; - // //uint16_t sinkNodeId[2]; - // } tokTypeStackGpProxyTableEntry; - - context.coreKeyType = SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY; - context.keyIndex = index; - - [plaintextKey, status] = (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) { - // typedef struct { - // uint8_t status; - // uint16_t options; - // //EmberGpAddress gpd; - // uint8_t gpAddress[8]; - // uint8_t endpoint; - // uint8_t securityOptions; - // uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t sinkType[2]; - // uint16_t groupList[2][2]; - // uint32_t securityFrameCounter; // This is no more used, Incoming FC for gpd in a separate Token to control its update. - // uint16_t assignedAlias; - // uint8_t deviceId; - // uint8_t groupcastRadius; - // } tokTypeStackGpSinkTableEntry; - - context.coreKeyType = SecManKeyType.GREEN_POWER_SINK_TABLE_KEY; - context.keyIndex = index; - - [plaintextKey, status] = (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) { - // typedef struct { - // uint32_t bitmask; - // uint8_t keyIndex; - // uint8_t encryptionKey[EMBER_ENCRYPTION_KEY_SIZE]; - // uint8_t preconfiguredKey[EMBER_ENCRYPTION_KEY_SIZE]; - // } EmberTokTypeStackZllSecurity; - - context.coreKeyType = SecManKeyType.ZLL_ENCRYPTION_KEY; - - [plaintextKey, status] = (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)); - - tokenData.data.set(plaintextKey.contents, 4 + 1 + EMBER_ENCRYPTION_KEY_SIZE);// uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE] - } else { - //nothing needs to be done for non-key tokens - } - - return status; - } - - /** - * - * @param data_s EmberTokenData* - * @param nvm3Key uint32_t - * @param index uint8_t - * @returns - * - * @from sli_zigbee_af_trust_center_backup_restore_keys_from_data - */ - private static async restoreKeysFromData(ezsp: Ezsp, tokenData: EmberTokenData, nvm3Key: number, index: number): Promise { - let status: SLStatus = SLStatus.OK; - const context = initSecurityManagerContext(); - - const plaintextKey: SecManKey = {contents: null}; - - if (nvm3Key === NVM3KEY_STACK_KEYS) { - // typedef struct { - // uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t activeKeySeqNum; - // } tokTypeStackKeys; - - context.coreKeyType = SecManKeyType.NETWORK; - context.keyIndex = 0; - plaintextKey.contents = tokenData.data.subarray(0, EMBER_ENCRYPTION_KEY_SIZE);// at beginning - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_ALTERNATE_KEY) { - // typedef struct { - // uint8_t networkKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t activeKeySeqNum; - // } tokTypeStackKeys; - - context.coreKeyType = SecManKeyType.NETWORK; - context.keyIndex = 1; - plaintextKey.contents = tokenData.data.subarray(0, EMBER_ENCRYPTION_KEY_SIZE);// at beginning - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_TRUST_CENTER) { - // typedef struct { - // uint16_t mode; - // uint8_t eui64[8]; - // uint8_t key[16]; // ignored if (mode & TRUST_CENTER_KEY_LIVES_IN_PSA) - // } tokTypeStackTrustCenter; - - context.coreKeyType = SecManKeyType.TC_LINK; - const s = 2 + EUI64_SIZE; - plaintextKey.contents = tokenData.data.subarray(s, s + EMBER_ENCRYPTION_KEY_SIZE);// uint16_t+uint8_t[8] - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_KEY_TABLE) { - // typedef uint8_t tokTypeStackKeyTable[25]; - - context.coreKeyType = SecManKeyType.APP_LINK; - context.keyIndex = index; - context.flags |= SecManFlag.KEY_INDEX_IS_VALID; - plaintextKey.contents = tokenData.data.subarray( - KEY_ENTRY_KEY_DATA_OFFSET, - KEY_ENTRY_KEY_DATA_OFFSET + EMBER_ENCRYPTION_KEY_SIZE - );// end part of uint8_t[25] - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_GP_PROXY_TABLE) { - // typedef struct { - // uint8_t status; - // uint32_t options; - // //EmberGpAddress gpd; - // uint8_t gpAddress[8]; - // uint8_t endpoint; - // //uint16_t assignedAlias; - // uint8_t securityOptions; - // uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // //EmberGpSinkListEntry sinkList[2]; - // uint8_t sinkType[2]; - // uint8_t sinkEUI[2][8]; - // //uint16_t sinkNodeId[2]; - // } tokTypeStackGpProxyTableEntry; - - context.coreKeyType = SecManKeyType.GREEN_POWER_PROXY_TABLE_KEY; - context.keyIndex = index; - const s = 1 + 4 + 8 + 1 + 1; - plaintextKey.contents = tokenData.data.subarray(s, s + EMBER_ENCRYPTION_KEY_SIZE);// uint8_t+uint32_t+uint8_t[8]+uint8_t+uint8_t - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_GP_SINK_TABLE) { - // typedef struct { - // uint8_t status; - // uint16_t options; - // //EmberGpAddress gpd; - // uint8_t gpAddress[8]; - // uint8_t endpoint; - // uint8_t securityOptions; - // uint8_t gpdKey[16]; // ignored if using Secure Key Storage (but moved to PSA and cleared if upgrade code is run) - // uint8_t sinkType[2]; - // uint16_t groupList[2][2]; - // uint32_t securityFrameCounter; // This is no more used, Incoming FC for gpd in a separate Token to control its update. - // uint16_t assignedAlias; - // uint8_t deviceId; - // uint8_t groupcastRadius; - // } tokTypeStackGpSinkTableEntry; - - context.coreKeyType = SecManKeyType.GREEN_POWER_SINK_TABLE_KEY; - context.keyIndex = index; - const s = 1 + 2 + 8 + 1 + 1; - plaintextKey.contents = tokenData.data.subarray(s, s + EMBER_ENCRYPTION_KEY_SIZE);// uint8_t+uint16_t+uint8_t[8]+uint8_t+uint8_t - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else if (nvm3Key === NVM3KEY_STACK_ZLL_SECURITY) { - // typedef struct { - // uint32_t bitmask; - // uint8_t keyIndex; - // uint8_t encryptionKey[EMBER_ENCRYPTION_KEY_SIZE]; - // uint8_t preconfiguredKey[EMBER_ENCRYPTION_KEY_SIZE]; - // } EmberTokTypeStackZllSecurity; - - context.coreKeyType = SecManKeyType.ZLL_ENCRYPTION_KEY; - let s = 4 + 1; - plaintextKey.contents = tokenData.data.subarray(s, s + EMBER_ENCRYPTION_KEY_SIZE);// uint32_t+uint8_t - - status = await ezsp.ezspImportKey(context, plaintextKey); - - context.coreKeyType = SecManKeyType.ZLL_PRECONFIGURED_KEY; - s += EMBER_ENCRYPTION_KEY_SIZE;// after `encryptionKey` - plaintextKey.contents = tokenData.data.subarray(s, s + EMBER_ENCRYPTION_KEY_SIZE);// uint32_t+uint8_t+uint8_t[EMBER_ENCRYPTION_KEY_SIZE] - - status = await ezsp.ezspImportKey(context, plaintextKey); - } else { - // unknown key - } - - return status; - } - - /** - * Updates zigbeed tokens from a backup of NCP tokens. - * - * @return EmberStatus status code - */ - public static async writeNcpTokensToZigbeedTokens(ezsp: Ezsp, inBuffer: Buffer): Promise { - if (!inBuffer?.length) { - throw new Error(`[TOKENS] Restore tokens buffer empty.`); - } - - console.log(`[TOKENS] Restoring tokens to Zigbeed...`); - - let readOffset: number = 0; - const inTokenCount = inBuffer.readUInt8(readOffset++); - - for (let i = 0; i < inTokenCount; i++) { - const nvm3Key = inBuffer.readUInt32LE(readOffset);// 4 bytes Token Key/Creator - readOffset += 4; - const size = inBuffer.readUInt8(readOffset++);// 1 byte token size - const arraySize = inBuffer.readUInt8(readOffset++);// 1 byte array size. - - for (let arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { - const tokenData: EmberTokenData = { - data: inBuffer.subarray(readOffset, readOffset + size), - size, - }; - - const creator = EmberTokensManager.getCreatorFromNvm3Key(nvm3Key);// uint16_t - const status = (await ezsp.ezspSetTokenData(creator, arrayIndex, tokenData)); - - console.assert( - status === EmberStatus.SUCCESS, - `[TOKENS] Failed to set Zigbeed token data for key "${nvm3Key}" creator "${creator}" with status=${EmberStatus[status]}.` - ); - - readOffset += tokenData.size; - } - } - - return EmberStatus.SUCCESS; - } -} diff --git a/src/adapter/ember/consts.ts b/src/adapter/ember/consts.ts deleted file mode 100644 index 443859c2865..00000000000 --- a/src/adapter/ember/consts.ts +++ /dev/null @@ -1,290 +0,0 @@ -//------------------------------------------------------------------------------------------------- -// General - -/** Endpoint profile ID */ -export const CBA_PROFILE_ID = 0x0105; -/** Endpoint profile ID for Zigbee 3.0. "Home Automation" */ -export const HA_PROFILE_ID = 0x0104; -/** Endpoint profile ID for Smart Energy */ -export const SE_PROFILE_ID = 0x0109; -/** Endpoint profile ID for Green Power */ -export const GP_PROFILE_ID = 0xA1E0; -/** The touchlink (ZigBee Light Link/ZLL) Profile ID. */ -export const TOUCHLINK_PROFILE_ID = 0xC05E; -/** The profile ID used to address all the public profiles. */ -export const WILDCARD_PROFILE_ID = 0xFFFF; - -/** Ember Corporation's Manufacturer ID allocated by the Zigbee alliance. This shall not change. */ -export const EMBER_COMPANY_MANUFACTURER_CODE = 0x1002; - -/** - * The MFG code set by AppBuilder for use in the App Framework (AF). - * If not set by AppBuilder we default it to 0x0000. The customer should be setting this value. - */ -export const MANUFACTURER_CODE = 0x1049; - -/** The network ID of the coordinator in a ZigBee network is 0x0000. */ -export const ZIGBEE_COORDINATOR_ADDRESS = 0x0000; - -/** A blank (also used as "wildcard") EUI64 hex string prefixed with 0x */ -export const BLANK_EUI64 = "0xFFFFFFFFFFFFFFFF"; -/** A blank extended PAN ID. (null/not present) */ -export const BLANK_EXTENDED_PAN_ID: readonly number[] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; -/** An invalid profile ID. This is a reserved profileId. */ -export const INVALID_PROFILE_ID = 0xFFFF; -/** An invalid cluster ID. */ -export const INVALID_CLUSTER_ID = 0xFFFF; -/** An invalid PAN ID. */ -export const INVALID_PAN_ID = 0xFFFF; -/** Serves to initialize cache */ -export const INVALID_NODE_TYPE = 0xFF; -/** Serves to initialize cache for config IDs */ -export const INVALID_CONFIG_VALUE = 0xFFFF; -/** Serves to initialize cache */ -export const INVALID_RADIO_CHANNEL = 0xFF; -/** A distinguished network ID that will never be assigned to any node. It is used to indicate the absence of a node ID. */ -export const NULL_NODE_ID = 0xFFFF; -export const UNKNOWN_NETWORK_STATE = 0xFF; -/** A distinguished binding index used to indicate the absence of a binding. */ -export const NULL_BINDING = 0xFF; -/** - * A distinguished network ID that will never be assigned to any node. - * This value is returned when getting the remote node ID from the binding table and the given binding table index refers - * to a multicast binding entry. - */ -export const EMBER_MULTICAST_NODE_ID = 0xFFFE; -/** - * A distinguished network ID that will never be assigned - * to any node. This value is used when getting the remote node ID - * from the address or binding tables. It indicates that the address - * or binding table entry is currently in use but the node ID - * corresponding to the EUI64 in the table is currently unknown. - */ -export const EMBER_UNKNOWN_NODE_ID = 0xFFFD; -/** - * A distinguished network ID that will never be assigned - * to any node. This value is used when getting the remote node ID - * from the address or binding tables. It indicates that the address - * or binding table entry is currently in use and network address - * discovery is underway. - */ -export const EMBER_DISCOVERY_ACTIVE_NODE_ID = 0xFFFC; -/** A distinguished address table index used to indicate the absence of an address table entry. */ -export const EMBER_NULL_ADDRESS_TABLE_INDEX = 0xFF; -/** Invalidates cached information */ -export const SOURCE_ROUTE_OVERHEAD_UNKNOWN = 0xFF; - -// Permit join times. -export const PERMIT_JOIN_FOREVER = 0xFF; -export const PERMIT_JOIN_MAX_TIMEOUT = 0xFE; - -//------------------------------------------------------------------------------------------------- -// Network - -/** - * ZigBee Broadcast Addresses - * - * ZigBee specifies three different broadcast addresses that - * reach different collections of nodes. Broadcasts are normally sent only - * to routers. Broadcasts can also be forwarded to end devices, either - * all of them or only those that do not sleep. Broadcasting to end - * devices is both significantly more resource-intensive and significantly - * less reliable than broadcasting to routers. - */ -/** Broadcast to all routers. */ -export const EMBER_BROADCAST_ADDRESS = 0xFFFC; -/** Broadcast to all non-sleepy devices. */ -export const EMBER_RX_ON_WHEN_IDLE_BROADCAST_ADDRESS = 0xFFFD; -/** Broadcast to all devices, including sleepy end devices. */ -export const EMBER_SLEEPY_BROADCAST_ADDRESS = 0xFFFF; -// From table 3.51 of 053474r14 -// When sending many-to-one route requests, the following -// addresses are used -// 0xFFF9 indicates a non-memory-constrained many-to-one route request -// 0xFFF8 indicates a memory-constrained many-to-one route request -export const EMBER_MIN_BROADCAST_ADDRESS = 0xFFF8; - -/** The maximum 802.15.4 channel number is 26. */ -export const EMBER_MAX_802_15_4_CHANNEL_NUMBER = 26; -/** The minimum 2.4GHz 802.15.4 channel number is 11. */ -export const EMBER_MIN_802_15_4_CHANNEL_NUMBER = 11; -/** The minimum SubGhz channel number is 0. */ -export const EMBER_MIN_SUBGHZ_CHANNEL_NUMBER = 0; - -/** - * ZigBee protocol specifies that active scans have a duration of 3 (138 msec). - * See documentation for emberStartScan in include/network-formation.h - * for more info on duration values. - */ -export const EMBER_ACTIVE_SCAN_DURATION = 3; -/** The SubGhz scan duration is 5. */ -export const EMBER_SUB_GHZ_SCAN_DURATION = 5; -/** There are sixteen 802.15.4 channels. */ -export const EMBER_NUM_802_15_4_CHANNELS = (EMBER_MAX_802_15_4_CHANNEL_NUMBER - EMBER_MIN_802_15_4_CHANNEL_NUMBER + 1); -/** A bitmask to scan all 2.4 GHz 802.15.4 channels. */ -export const EMBER_ALL_802_15_4_CHANNELS_MASK = 0x07FFF800; -/** The channels that the plugin will preferentially scan when forming and joining. */ -export const NETWORK_FIND_CHANNEL_MASK = 0x0318C800; -/** - * Cut-off value (dBm) <-128..127> - * The maximum noise allowed on a channel to consider for forming a network. - * If the noise on all preferred channels is above this limit and "Enable scanning all channels" is ticked, the scan continues on all channels. - * Use emberAfPluginNetworkFindGetEnergyThresholdForChannelCallback() to override this value. - */ -export const NETWORK_FIND_CUT_OFF_VALUE = -48; - -/** - * The additional overhead required for network source routing (relay count = 1, relay index = 1). - * This does not include the size of the relay list itself. - */ -export const NWK_SOURCE_ROUTE_OVERHEAD = 2; -export const SOURCE_ROUTING_RESERVED_PAYLOAD_LENGTH = 0; -/** - * The maximum APS payload, not including any APS options. - * This value is also available from emberMaximumApsPayloadLength() or ezspMaximumPayloadLength(). - * See http://portal.ember.com/faq/payload for more information. - */ -export const MAXIMUM_APS_PAYLOAD_LENGTH = (82 - SOURCE_ROUTING_RESERVED_PAYLOAD_LENGTH); -// export const MAXIMUM_APS_PAYLOAD_LENGTH_SECURITY_NONE = (100 - SOURCE_ROUTING_RESERVED_PAYLOAD_LENGTH); -/** The additional overhead required for APS encryption (security = 5, MIC = 4). */ -export const APS_ENCRYPTION_OVERHEAD = 9; -/** The additional overhead required for APS fragmentation. */ -export const APS_FRAGMENTATION_OVERHEAD = 2; - -/** - * A concentrator with insufficient memory to store source routes for the entire network. - * Route records are sent to the concentrator prior to every inbound APS unicast. - */ -export const EMBER_LOW_RAM_CONCENTRATOR = 0xFFF8; -/** - * A concentrator with sufficient memory to store source routes for the entire network. - * Remote nodes stop sending route records once the concentrator has successfully received one. - */ -export const EMBER_HIGH_RAM_CONCENTRATOR = 0xFFF9; - - -//------------------------------------------------------------------------------------------------- -// Security - -/** The short address of the trust center. This address never changes dynamically. */ -export const EMBER_TRUST_CENTER_NODE_ID = 0x0000; - - -/** The size of the CRC that is appended to an installation code. */ -export const EMBER_INSTALL_CODE_CRC_SIZE = 2; - -/** The number of sizes of acceptable installation codes used in Certificate Based Key Establishment (CBKE). */ -export const EMBER_NUM_INSTALL_CODE_SIZES = 4; - -/** - * Various sizes of valid installation codes that are stored in the manufacturing tokens. - * Note that each size includes 2 bytes of CRC appended to the end of the installation code. - */ -export const EMBER_INSTALL_CODE_SIZES = [ - 6 + EMBER_INSTALL_CODE_CRC_SIZE, - 8 + EMBER_INSTALL_CODE_CRC_SIZE, - 12 + EMBER_INSTALL_CODE_CRC_SIZE, - 16 + EMBER_INSTALL_CODE_CRC_SIZE -]; - -/** - * Default value for context's PSA algorithm permission (CCM* with 4 byte tag). - * Only used by NCPs with secure key storage; define is mirrored here to allow - * host code to initialize the context itself rather than needing a new EZSP frame. - */ -export const ZB_PSA_ALG = 0x05440100; - -export const STACK_PROFILE_ZIGBEE_PRO = 0x02; -export const SECURITY_LEVEL_Z3 = 0x05; - -/** This key is "ZigBeeAlliance09" */ -export const ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY: readonly number[] = [ - 0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C, 0x69, 0x61, 0x6E, 0x63, 0x65, 0x30, 0x39 -]; - - - -//------------------------------------------------------------------------------------------------- -// Zigbee Green Power types and defines. - -/** The GP endpoint, as defined in the ZigBee spec. */ -export const GP_ENDPOINT = 0xF2; - -/** Number of GP sink list entries. Minimum is 2 sink list entries. */ -export const GP_SINK_LIST_ENTRIES = 2; -/** The size of the SinkList entries in sink table in format of octet string that has a format of {<1 byte length>, } */ -export const GP_SIZE_OF_SINK_LIST_ENTRIES_OCTET_STRING = (1 + (GP_SINK_LIST_ENTRIES * 4));// sizeof(EmberGpSinkGroup) === uint16_t * 2 - - -//------------------------------------------------------------------------------------------------- -//-- InterPAN - -// Max PHY size = 128 -// -1 byte for PHY length -// -2 bytes for MAC CRC -export const MAXIMUM_INTERPAN_LENGTH = 125; - -// MAC frame control -// Bits: -// | 0-2 | 3 | 4 | 5 | 6 | 7-9 | 10-11 | 12-13 | 14-15 | -// | Frame | Security | Frame | Ack | Intra | Reserved | Dest. | Reserved | Src | -// | Type | Enabled | Pending | Req | PAN | | Addr. | | Adrr. | -// | | | | | | | Mode | | Mode | - -// Frame Type -// 000 = Beacon -// 001 = Data -// 010 = Acknwoledgement -// 011 = MAC Command -// 100 - 111 = Reserved - -// Addressing Mode -// 00 - PAN ID and address field are not present -// 01 - Reserved -// 10 - Address field contains a 16-bit short address -// 11 - Address field contains a 64-bit extended address - -const MAC_FRAME_TYPE_DATA = 0x0001; -// const MAC_FRAME_SOURCE_MODE_SHORT = 0x8000; -const MAC_FRAME_SOURCE_MODE_LONG = 0xC000; -const MAC_FRAME_DESTINATION_MODE_SHORT = 0x0800; -const MAC_FRAME_DESTINATION_MODE_LONG = 0x0C00; - -// The two possible incoming MAC frame controls. -// Using short source address is not allowed. -export const SHORT_DEST_FRAME_CONTROL = (MAC_FRAME_TYPE_DATA | MAC_FRAME_DESTINATION_MODE_SHORT | MAC_FRAME_SOURCE_MODE_LONG); -export const LONG_DEST_FRAME_CONTROL = (MAC_FRAME_TYPE_DATA | MAC_FRAME_DESTINATION_MODE_LONG | MAC_FRAME_SOURCE_MODE_LONG); - -export const MAC_ACK_REQUIRED = 0x0020; - -/** NWK stub frame has two control bytes. */ -export const STUB_NWK_SIZE = 2; -export const STUB_NWK_FRAME_CONTROL = 0x000B; - -/** - * Interpan APS Unicast, same for Broadcast. - * - Frame Control (1-byte) - * - Cluster ID (2-bytes) - * - Profile ID (2-bytes) - */ -export const INTERPAN_APS_UNICAST_BROADCAST_SIZE = 5; -/** - * Interpan APS Multicast - * - Frame Control (1-byte) - * - Group ID (2-bytes) - * - Cluster ID (2-bytes) - * - Profile ID (2-bytes) - */ -export const INTERPAN_APS_MULTICAST_SIZE = 7; - -export const MAX_STUB_APS_SIZE = (INTERPAN_APS_MULTICAST_SIZE); -export const MIN_STUB_APS_SIZE = (INTERPAN_APS_UNICAST_BROADCAST_SIZE); - -export const INTERPAN_APS_FRAME_TYPE = 0x03; -export const INTERPAN_APS_FRAME_TYPE_MASK = 0x03; - -/** The only allowed APS FC value (without the delivery mode subfield) */ -export const INTERPAN_APS_FRAME_CONTROL_NO_DELIVERY_MODE = (INTERPAN_APS_FRAME_TYPE); - -export const INTERPAN_APS_FRAME_DELIVERY_MODE_MASK = 0x0C; -export const INTERPAN_APS_FRAME_SECURITY = 0x20; diff --git a/src/adapter/ember/enums.ts b/src/adapter/ember/enums.ts deleted file mode 100644 index 6de4aac0c84..00000000000 --- a/src/adapter/ember/enums.ts +++ /dev/null @@ -1,2423 +0,0 @@ - -/** Status Defines */ -export enum SLStatus { - // ----------------------------------------------------------------------------- - // Generic Errors - - /** No error. */ - OK = 0x0000, - /** Generic error. */ - FAIL = 0x0001, - - // ----------------------------------------------------------------------------- - // State Errors - - /** Generic invalid state error. */ - INVALID_STATE = 0x0002, - /** Module is not ready for requested operation. */ - NOT_READY = 0x0003, - /** Module is busy and cannot carry out requested operation. */ - BUSY = 0x0004, - /** Operation is in progress and not yet complete (pass or fail). */ - IN_PROGRESS = 0x0005, - /** Operation aborted. */ - ABORT = 0x0006, - /** Operation timed out. */ - TIMEOUT = 0x0007, - /** Operation not allowed per permissions. */ - PERMISSION = 0x0008, - /** Non-blocking operation would block. */ - WOULD_BLOCK = 0x0009, - /** Operation/module is Idle, cannot carry requested operation. */ - IDLE = 0x000A, - /** Operation cannot be done while construct is waiting. */ - IS_WAITING = 0x000B, - /** No task/construct waiting/pending for that action/event. */ - NONE_WAITING = 0x000C, - /** Operation cannot be done while construct is suspended. */ - SUSPENDED = 0x000D, - /** Feature not available due to software configuration. */ - NOT_AVAILABLE = 0x000E, - /** Feature not supported. */ - NOT_SUPPORTED = 0x000F, - /** Initialization failed. */ - INITIALIZATION = 0x0010, - /** Module has not been initialized. */ - NOT_INITIALIZED = 0x0011, - /** Module has already been initialized. */ - ALREADY_INITIALIZED = 0x0012, - /** Object/construct has been deleted. */ - DELETED = 0x0013, - /** Illegal call from ISR. */ - ISR = 0x0014, - /** Illegal call because network is up. */ - NETWORK_UP = 0x0015, - /** Illegal call because network is down. */ - NETWORK_DOWN = 0x0016, - /** Failure due to not being joined in a network. */ - NOT_JOINED = 0x0017, - /** Invalid operation as there are no beacons. */ - NO_BEACONS = 0x0018, - - // ----------------------------------------------------------------------------- - // Allocation/ownership Errors - - /** Generic allocation error. */ - ALLOCATION_FAILED = 0x0019, - /** No more resource available to perform the operation. */ - NO_MORE_RESOURCE = 0x001A, - /** Item/list/queue is empty. */ - EMPTY = 0x001B, - /** Item/list/queue is full. */ - FULL = 0x001C, - /** Item would overflow. */ - WOULD_OVERFLOW = 0x001D, - /** Item/list/queue has been overflowed. */ - HAS_OVERFLOWED = 0x001E, - /** Generic ownership error. */ - OWNERSHIP = 0x001F, - /** Already/still owning resource. */ - IS_OWNER = 0x0020, - - // ----------------------------------------------------------------------------- - // Invalid Parameters Errors - - /** Generic invalid argument or consequence of invalid argument. */ - INVALID_PARAMETER = 0x0021, - /** Invalid null pointer received as argument. */ - NULL_POINTER = 0x0022, - /** Invalid configuration provided. */ - INVALID_CONFIGURATION = 0x0023, - /** Invalid mode. */ - INVALID_MODE = 0x0024, - /** Invalid handle. */ - INVALID_HANDLE = 0x0025, - /** Invalid type for operation. */ - INVALID_TYPE = 0x0026, - /** Invalid index. */ - INVALID_INDEX = 0x0027, - /** Invalid range. */ - INVALID_RANGE = 0x0028, - /** Invalid key. */ - INVALID_KEY = 0x0029, - /** Invalid credentials. */ - INVALID_CREDENTIALS = 0x002A, - /** Invalid count. */ - INVALID_COUNT = 0x002B, - /** Invalid signature / verification failed. */ - INVALID_SIGNATURE = 0x002C, - /** Item could not be found. */ - NOT_FOUND = 0x002D, - /** Item already exists. */ - ALREADY_EXISTS = 0x002E, - - // ----------------------------------------------------------------------------- - // IO/Communication Errors - - /** Generic I/O failure. */ - IO = 0x002F, - /** I/O failure due to timeout. */ - IO_TIMEOUT = 0x0030, - /** Generic transmission error. */ - TRANSMIT = 0x0031, - /** Transmit underflowed. */ - TRANSMIT_UNDERFLOW = 0x0032, - /** Transmit is incomplete. */ - TRANSMIT_INCOMPLETE = 0x0033, - /** Transmit is busy. */ - TRANSMIT_BUSY = 0x0034, - /** Generic reception error. */ - RECEIVE = 0x0035, - /** Failed to read on/via given object. */ - OBJECT_READ = 0x0036, - /** Failed to write on/via given object. */ - OBJECT_WRITE = 0x0037, - /** Message is too long. */ - MESSAGE_TOO_LONG = 0x0038, - - // ----------------------------------------------------------------------------- - // EEPROM/Flash Errors - - /** EEPROM MFG version mismatch. */ - EEPROM_MFG_VERSION_MISMATCH = 0x0039, - /** EEPROM Stack version mismatch. */ - EEPROM_STACK_VERSION_MISMATCH = 0x003A, - /** Flash write is inhibited. */ - FLASH_WRITE_INHIBITED = 0x003B, - /** Flash verification failed. */ - FLASH_VERIFY_FAILED = 0x003C, - /** Flash programming failed. */ - FLASH_PROGRAM_FAILED = 0x003D, - /** Flash erase failed. */ - FLASH_ERASE_FAILED = 0x003E, - - // ----------------------------------------------------------------------------- - // MAC Errors - - /** MAC no data. */ - MAC_NO_DATA = 0x003F, - /** MAC no ACK received. */ - MAC_NO_ACK_RECEIVED = 0x0040, - /** MAC indirect timeout. */ - MAC_INDIRECT_TIMEOUT = 0x0041, - /** MAC unknown header type. */ - MAC_UNKNOWN_HEADER_TYPE = 0x0042, - /** MAC ACK unknown header type. */ - MAC_ACK_HEADER_TYPE = 0x0043, - /** MAC command transmit failure. */ - MAC_COMMAND_TRANSMIT_FAILURE = 0x0044, - - // ----------------------------------------------------------------------------- - // CLI_STORAGE Errors - - /** Error in open NVM */ - CLI_STORAGE_NVM_OPEN_ERROR = 0x0045, - - // ----------------------------------------------------------------------------- - // Security status codes - - /** Image checksum is not valid. */ - SECURITY_IMAGE_CHECKSUM_ERROR = 0x0046, - /** Decryption failed */ - SECURITY_DECRYPT_ERROR = 0x0047, - - // ----------------------------------------------------------------------------- - // Command status codes - - /** Command was not recognized */ - COMMAND_IS_INVALID = 0x0048, - /** Command or parameter maximum length exceeded */ - COMMAND_TOO_LONG = 0x0049, - /** Data received does not form a complete command */ - COMMAND_INCOMPLETE = 0x004A, - - // ----------------------------------------------------------------------------- - // Misc Errors - - /** Bus error, e.g. invalid DMA address */ - BUS_ERROR = 0x004B, - - // ----------------------------------------------------------------------------- - // Unified MAC Errors - - /** CCA failure. */ - CCA_FAILURE = 0x004C, - - // ----------------------------------------------------------------------------- - // Scan errors - - /** MAC scanning. */ - MAC_SCANNING = 0x004D, - /** MAC incorrect scan type. */ - MAC_INCORRECT_SCAN_TYPE = 0x004E, - /** Invalid channel mask. */ - INVALID_CHANNEL_MASK = 0x004F, - /** Bad scan duration. */ - BAD_SCAN_DURATION = 0x0050, - - // ----------------------------------------------------------------------------- - // Bluetooth status codes - - /** Bonding procedure can't be started because device has no space left for bond. */ - BT_OUT_OF_BONDS = 0x0402, - /** Unspecified error */ - BT_UNSPECIFIED = 0x0403, - /** Hardware failure */ - BT_HARDWARE = 0x0404, - /** The bonding does not exist. */ - BT_NO_BONDING = 0x0406, - /** Error using crypto functions */ - BT_CRYPTO = 0x0407, - /** Data was corrupted. */ - BT_DATA_CORRUPTED = 0x0408, - /** Invalid periodic advertising sync handle */ - BT_INVALID_SYNC_HANDLE = 0x040A, - /** Bluetooth cannot be used on this hardware */ - BT_INVALID_MODULE_ACTION = 0x040B, - /** Error received from radio */ - BT_RADIO = 0x040C, - /** Returned when remote disconnects the connection-oriented channel by sending disconnection request. */ - BT_L2CAP_REMOTE_DISCONNECTED = 0x040D, - /** Returned when local host disconnect the connection-oriented channel by sending disconnection request. */ - BT_L2CAP_LOCAL_DISCONNECTED = 0x040E, - /** Returned when local host did not find a connection-oriented channel with given destination CID. */ - BT_L2CAP_CID_NOT_EXIST = 0x040F, - /** Returned when connection-oriented channel disconnected due to LE connection is dropped. */ - BT_L2CAP_LE_DISCONNECTED = 0x0410, - /** Returned when connection-oriented channel disconnected due to remote end send data even without credit. */ - BT_L2CAP_FLOW_CONTROL_VIOLATED = 0x0412, - /** Returned when connection-oriented channel disconnected due to remote end send flow control credits exceed 65535. */ - BT_L2CAP_FLOW_CONTROL_CREDIT_OVERFLOWED = 0x0413, - /** Returned when connection-oriented channel has run out of flow control credit and local application still trying to send data. */ - BT_L2CAP_NO_FLOW_CONTROL_CREDIT = 0x0414, - /** Returned when connection-oriented channel has not received connection response message within maximum timeout. */ - BT_L2CAP_CONNECTION_REQUEST_TIMEOUT = 0x0415, - /** Returned when local host received a connection-oriented channel connection response with an invalid destination CID. */ - BT_L2CAP_INVALID_CID = 0x0416, - /** Returned when local host application tries to send a command which is not suitable for L2CAP channel's current state. */ - BT_L2CAP_WRONG_STATE = 0x0417, - /** Flash reserved for PS store is full */ - BT_PS_STORE_FULL = 0x041B, - /** PS key not found */ - BT_PS_KEY_NOT_FOUND = 0x041C, - /** Mismatched or insufficient security level */ - BT_APPLICATION_MISMATCHED_OR_INSUFFICIENT_SECURITY = 0x041D, - /** Encryption/decryption operation failed. */ - BT_APPLICATION_ENCRYPTION_DECRYPTION_ERROR = 0x041E, - - // ----------------------------------------------------------------------------- - // Bluetooth controller status codes - - /** Connection does not exist, or connection open request was cancelled. */ - BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER = 0x1002, - /** - * Pairing or authentication failed due to incorrect results in the pairing or authentication procedure. - * This could be due to an incorrect PIN or Link Key - */ - BT_CTRL_AUTHENTICATION_FAILURE = 0x1005, - /** Pairing failed because of missing PIN, or authentication failed because of missing Key */ - BT_CTRL_PIN_OR_KEY_MISSING = 0x1006, - /** Controller is out of memory. */ - BT_CTRL_MEMORY_CAPACITY_EXCEEDED = 0x1007, - /** Link supervision timeout has expired. */ - BT_CTRL_CONNECTION_TIMEOUT = 0x1008, - /** Controller is at limit of connections it can support. */ - BT_CTRL_CONNECTION_LIMIT_EXCEEDED = 0x1009, - /** - * The Synchronous Connection Limit to a Device Exceeded error code indicates that the Controller has reached - * the limit to the number of synchronous connections that can be achieved to a device. - */ - BT_CTRL_SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x100A, - /** - * The ACL Connection Already Exists error code indicates that an attempt to create a new ACL Connection - * to a device when there is already a connection to this device. - */ - BT_CTRL_ACL_CONNECTION_ALREADY_EXISTS = 0x100B, - /** Command requested cannot be executed because the Controller is in a state where it cannot process this command at this time. */ - BT_CTRL_COMMAND_DISALLOWED = 0x100C, - /** The Connection Rejected Due To Limited Resources error code indicates that an incoming connection was rejected due to limited resources. */ - BT_CTRL_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES = 0x100D, - /** - * The Connection Rejected Due To Security Reasons error code indicates that a connection was rejected due - * to security requirements not being fulfilled, like authentication or pairing. - */ - BT_CTRL_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS = 0x100E, - /** - * The Connection was rejected because this device does not accept the BD_ADDR. - * This may be because the device will only accept connections from specific BD_ADDRs. - */ - BT_CTRL_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR = 0x100F, - /** The Connection Accept Timeout has been exceeded for this connection attempt. */ - BT_CTRL_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED = 0x1010, - /** A feature or parameter value in the HCI command is not supported. */ - BT_CTRL_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE = 0x1011, - /** Command contained invalid parameters. */ - BT_CTRL_INVALID_COMMAND_PARAMETERS = 0x1012, - /** User on the remote device terminated the connection. */ - BT_CTRL_REMOTE_USER_TERMINATED = 0x1013, - /** The remote device terminated the connection because of low resources */ - BT_CTRL_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES = 0x1014, - /** Remote Device Terminated Connection due to Power Off */ - BT_CTRL_REMOTE_POWERING_OFF = 0x1015, - /** Local device terminated the connection. */ - BT_CTRL_CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x1016, - /** - * The Controller is disallowing an authentication or pairing procedure because too little time has elapsed - * since the last authentication or pairing attempt failed. - */ - BT_CTRL_REPEATED_ATTEMPTS = 0x1017, - /** - * The device does not allow pairing. This can be for example, when a device only allows pairing during - * a certain time window after some user input allows pairing - */ - BT_CTRL_PAIRING_NOT_ALLOWED = 0x1018, - /** The remote device does not support the feature associated with the issued command. */ - BT_CTRL_UNSUPPORTED_REMOTE_FEATURE = 0x101A, - /** No other error code specified is appropriate to use. */ - BT_CTRL_UNSPECIFIED_ERROR = 0x101F, - /** Connection terminated due to link-layer procedure timeout. */ - BT_CTRL_LL_RESPONSE_TIMEOUT = 0x1022, - /** LL procedure has collided with the same transaction or procedure that is already in progress. */ - BT_CTRL_LL_PROCEDURE_COLLISION = 0x1023, - /** The requested encryption mode is not acceptable at this time. */ - BT_CTRL_ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x1025, - /** Link key cannot be changed because a fixed unit key is being used. */ - BT_CTRL_LINK_KEY_CANNOT_BE_CHANGED = 0x1026, - /** LMP PDU or LL PDU that includes an instant cannot be performed because the instant when this would have occurred has passed. */ - BT_CTRL_INSTANT_PASSED = 0x1028, - /** It was not possible to pair as a unit key was requested and it is not supported. */ - BT_CTRL_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x1029, - /** LMP transaction was started that collides with an ongoing transaction. */ - BT_CTRL_DIFFERENT_TRANSACTION_COLLISION = 0x102A, - /** The Controller cannot perform channel assessment because it is not supported. */ - BT_CTRL_CHANNEL_ASSESSMENT_NOT_SUPPORTED = 0x102E, - /** The HCI command or LMP PDU sent is only possible on an encrypted link. */ - BT_CTRL_INSUFFICIENT_SECURITY = 0x102F, - /** A parameter value requested is outside the mandatory range of parameters for the given HCI command or LMP PDU. */ - BT_CTRL_PARAMETER_OUT_OF_MANDATORY_RANGE = 0x1030, - /** - * The IO capabilities request or response was rejected because the sending Host does not support - * Secure Simple Pairing even though the receiving Link Manager does. - */ - BT_CTRL_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST = 0x1037, - /** - * The Host is busy with another pairing operation and unable to support the requested pairing. - * The receiving device should retry pairing again later. - */ - BT_CTRL_HOST_BUSY_PAIRING = 0x1038, - /** The Controller could not calculate an appropriate value for the Channel selection operation. */ - BT_CTRL_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND = 0x1039, - /** Operation was rejected because the controller is busy and unable to process the request. */ - BT_CTRL_CONTROLLER_BUSY = 0x103A, - /** Remote device terminated the connection because of an unacceptable connection interval. */ - BT_CTRL_UNACCEPTABLE_CONNECTION_INTERVAL = 0x103B, - /** Advertising for a fixed duration completed or, for directed advertising, that advertising completed without a connection being created. */ - BT_CTRL_ADVERTISING_TIMEOUT = 0x103C, - /** Connection was terminated because the Message Integrity Check (MIC) failed on a received packet. */ - BT_CTRL_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE = 0x103D, - /** LL initiated a connection but the connection has failed to be established. Controller did not receive any packets from remote end. */ - BT_CTRL_CONNECTION_FAILED_TO_BE_ESTABLISHED = 0x103E, - /** The MAC of the 802.11 AMP was requested to connect to a peer, but the connection failed. */ - BT_CTRL_MAC_CONNECTION_FAILED = 0x103F, - /** - * The master, at this time, is unable to make a coarse adjustment to the piconet clock, using the supplied parameters. - * Instead the master will attempt to move the clock using clock dragging. - */ - BT_CTRL_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING = 0x1040, - /** A command was sent from the Host that should identify an Advertising or Sync handle, but the Advertising or Sync handle does not exist. */ - BT_CTRL_UNKNOWN_ADVERTISING_IDENTIFIER = 0x1042, - /** Number of operations requested has been reached and has indicated the completion of the activity (e.g., advertising or scanning). */ - BT_CTRL_LIMIT_REACHED = 0x1043, - /** A request to the Controller issued by the Host and still pending was successfully canceled. */ - BT_CTRL_OPERATION_CANCELLED_BY_HOST = 0x1044, - /** An attempt was made to send or receive a packet that exceeds the maximum allowed packet l */ - BT_CTRL_PACKET_TOO_LONG = 0x1045, - - // ----------------------------------------------------------------------------- - // Bluetooth attribute status codes - - /** The attribute handle given was not valid on this server */ - BT_ATT_INVALID_HANDLE = 0x1101, - /** The attribute cannot be read */ - BT_ATT_READ_NOT_PERMITTED = 0x1102, - /** The attribute cannot be written */ - BT_ATT_WRITE_NOT_PERMITTED = 0x1103, - /** The attribute PDU was invalid */ - BT_ATT_INVALID_PDU = 0x1104, - /** The attribute requires authentication before it can be read or written. */ - BT_ATT_INSUFFICIENT_AUTHENTICATION = 0x1105, - /** Attribute Server does not support the request received from the client. */ - BT_ATT_REQUEST_NOT_SUPPORTED = 0x1106, - /** Offset specified was past the end of the attribute */ - BT_ATT_INVALID_OFFSET = 0x1107, - /** The attribute requires authorization before it can be read or written. */ - BT_ATT_INSUFFICIENT_AUTHORIZATION = 0x1108, - /** Too many prepare writes have been queued */ - BT_ATT_PREPARE_QUEUE_FULL = 0x1109, - /** No attribute found within the given attribute handle range. */ - BT_ATT_ATT_NOT_FOUND = 0x110A, - /** The attribute cannot be read or written using the Read Blob Request */ - BT_ATT_ATT_NOT_LONG = 0x110B, - /** The Encryption Key Size used for encrypting this link is insufficient. */ - BT_ATT_INSUFFICIENT_ENC_KEY_SIZE = 0x110C, - /** The attribute value length is invalid for the operation */ - BT_ATT_INVALID_ATT_LENGTH = 0x110D, - /** The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. */ - BT_ATT_UNLIKELY_ERROR = 0x110E, - /** The attribute requires encryption before it can be read or written. */ - BT_ATT_INSUFFICIENT_ENCRYPTION = 0x110F, - /** The attribute type is not a supported grouping attribute as defined by a higher layer specification. */ - BT_ATT_UNSUPPORTED_GROUP_TYPE = 0x1110, - /** Insufficient Resources to complete the request */ - BT_ATT_INSUFFICIENT_RESOURCES = 0x1111, - /** The server requests the client to rediscover the database. */ - BT_ATT_OUT_OF_SYNC = 0x1112, - /** The attribute parameter value was not allowed. */ - BT_ATT_VALUE_NOT_ALLOWED = 0x1113, - /** When this is returned in a BGAPI response, the application tried to read or write the value of a user attribute from the GATT database. */ - BT_ATT_APPLICATION = 0x1180, - /** The requested write operation cannot be fulfilled for reasons other than permissions. */ - BT_ATT_WRITE_REQUEST_REJECTED = 0x11FC, - /** The Client Characteristic Configuration descriptor is not configured according to the requirements of the profile or service. */ - BT_ATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_IMPROPERLY_CONFIGURED = 0x11FD, - /** The profile or service request cannot be serviced because an operation that has been previously triggered is still in progress. */ - BT_ATT_PROCEDURE_ALREADY_IN_PROGRESS = 0x11FE, - /** The attribute value is out of range as defined by a profile or service specification. */ - BT_ATT_OUT_OF_RANGE = 0x11FF, - - // ----------------------------------------------------------------------------- - // Bluetooth Security Manager Protocol status codes - - /** The user input of passkey failed, for example, the user cancelled the operation */ - BT_SMP_PASSKEY_ENTRY_FAILED = 0x1201, - /** Out of Band data is not available for authentication */ - BT_SMP_OOB_NOT_AVAILABLE = 0x1202, - /** The pairing procedure cannot be performed as authentication requirements cannot be met due to IO capabilities of one or both devices */ - BT_SMP_AUTHENTICATION_REQUIREMENTS = 0x1203, - /** The confirm value does not match the calculated compare value */ - BT_SMP_CONFIRM_VALUE_FAILED = 0x1204, - /** Pairing is not supported by the device */ - BT_SMP_PAIRING_NOT_SUPPORTED = 0x1205, - /** The resultant encryption key size is insufficient for the security requirements of this device */ - BT_SMP_ENCRYPTION_KEY_SIZE = 0x1206, - /** The SMP command received is not supported on this device */ - BT_SMP_COMMAND_NOT_SUPPORTED = 0x1207, - /** Pairing failed due to an unspecified reason */ - BT_SMP_UNSPECIFIED_REASON = 0x1208, - /** Pairing or authentication procedure is disallowed because too little time has elapsed since last pairing request or security request */ - BT_SMP_REPEATED_ATTEMPTS = 0x1209, - /** The Invalid Parameters error code indicates: the command length is invalid or a parameter is outside of the specified range. */ - BT_SMP_INVALID_PARAMETERS = 0x120A, - /** Indicates to the remote device that the DHKey Check value received doesn't match the one calculated by the local device. */ - BT_SMP_DHKEY_CHECK_FAILED = 0x120B, - /** Indicates that the confirm values in the numeric comparison protocol do not match. */ - BT_SMP_NUMERIC_COMPARISON_FAILED = 0x120C, - /** Indicates that the pairing over the LE transport failed due to a Pairing Request sent over the BR/EDR transport in process. */ - BT_SMP_BREDR_PAIRING_IN_PROGRESS = 0x120D, - /** Indicates that the BR/EDR Link Key generated on the BR/EDR transport cannot be used to derive and distribute keys for the LE transport. */ - BT_SMP_CROSS_TRANSPORT_KEY_DERIVATION_GENERATION_NOT_ALLOWED = 0x120E, - /** Indicates that the device chose not to accept a distributed key. */ - BT_SMP_KEY_REJECTED = 0x120F, - - // ----------------------------------------------------------------------------- - // Bluetooth Mesh status codes - - /** Returned when trying to add a key or some other unique resource with an ID which already exists */ - BT_MESH_ALREADY_EXISTS = 0x0501, - /** Returned when trying to manipulate a key or some other resource with an ID which does not exist */ - BT_MESH_DOES_NOT_EXIST = 0x0502, - /** - * Returned when an operation cannot be executed because a pre-configured limit for keys, key bindings, - * elements, models, virtual addresses, provisioned devices, or provisioning sessions is reached - */ - BT_MESH_LIMIT_REACHED = 0x0503, - /** Returned when trying to use a reserved address or add a "pre-provisioned" device using an address already used by some other device */ - BT_MESH_INVALID_ADDRESS = 0x0504, - /** In a BGAPI response, the user supplied malformed data; in a BGAPI event, the remote end responded with malformed or unrecognized data */ - BT_MESH_MALFORMED_DATA = 0x0505, - /** An attempt was made to initialize a subsystem that was already initialized. */ - BT_MESH_ALREADY_INITIALIZED = 0x0506, - /** An attempt was made to use a subsystem that wasn't initialized yet. Call the subsystem's init function first. */ - BT_MESH_NOT_INITIALIZED = 0x0507, - /** Returned when trying to establish a friendship as a Low Power Node, but no acceptable friend offer message was received. */ - BT_MESH_NO_FRIEND_OFFER = 0x0508, - /** Provisioning link was unexpectedly closed before provisioning was complete. */ - BT_MESH_PROV_LINK_CLOSED = 0x0509, - /**An unrecognized provisioning PDU was received. */ - BT_MESH_PROV_INVALID_PDU = 0x050A, - /**A provisioning PDU with wrong length or containing field values that are out of bounds was received. */ - BT_MESH_PROV_INVALID_PDU_FORMAT = 0x050B, - /**An unexpected (out of sequence) provisioning PDU was received. */ - BT_MESH_PROV_UNEXPECTED_PDU = 0x050C, - /**The computed confirmation value did not match the expected value. */ - BT_MESH_PROV_CONFIRMATION_FAILED = 0x050D, - /**Provisioning could not be continued due to insufficient resources. */ - BT_MESH_PROV_OUT_OF_RESOURCES = 0x050E, - /**The provisioning data block could not be decrypted. */ - BT_MESH_PROV_DECRYPTION_FAILED = 0x050F, - /**An unexpected error happened during provisioning. */ - BT_MESH_PROV_UNEXPECTED_ERROR = 0x0510, - /**Device could not assign unicast addresses to all of its elements. */ - BT_MESH_PROV_CANNOT_ASSIGN_ADDR = 0x0511, - /**Returned when trying to reuse an address of a previously deleted device before an IV Index Update has been executed. */ - BT_MESH_ADDRESS_TEMPORARILY_UNAVAILABLE = 0x0512, - /**Returned when trying to assign an address that is used by one of the devices in the Device Database, or by the Provisioner itself. */ - BT_MESH_ADDRESS_ALREADY_USED = 0x0513, - /**Application key or publish address are not set */ - BT_MESH_PUBLISH_NOT_CONFIGURED = 0x0514, - /**Application key is not bound to a model */ - BT_MESH_APP_KEY_NOT_BOUND = 0x0515, - - // ----------------------------------------------------------------------------- - // Bluetooth Mesh foundation status codes - - /** Returned when address in request was not valid */ - BT_MESH_FOUNDATION_INVALID_ADDRESS = 0x1301, - /** Returned when model identified is not found for a given element */ - BT_MESH_FOUNDATION_INVALID_MODEL = 0x1302, - /** Returned when the key identified by AppKeyIndex is not stored in the node */ - BT_MESH_FOUNDATION_INVALID_APP_KEY = 0x1303, - /** Returned when the key identified by NetKeyIndex is not stored in the node */ - BT_MESH_FOUNDATION_INVALID_NET_KEY = 0x1304, - /** Returned when The node cannot serve the request due to insufficient resources */ - BT_MESH_FOUNDATION_INSUFFICIENT_RESOURCES = 0x1305, - /** Returned when the key identified is already stored in the node and the new NetKey value is different */ - BT_MESH_FOUNDATION_KEY_INDEX_EXISTS = 0x1306, - /** Returned when the model does not support the publish mechanism */ - BT_MESH_FOUNDATION_INVALID_PUBLISH_PARAMS = 0x1307, - /** Returned when the model does not support the subscribe mechanism */ - BT_MESH_FOUNDATION_NOT_SUBSCRIBE_MODEL = 0x1308, - /** Returned when storing of the requested parameters failed */ - BT_MESH_FOUNDATION_STORAGE_FAILURE = 0x1309, - /**Returned when requested setting is not supported */ - BT_MESH_FOUNDATION_NOT_SUPPORTED = 0x130A, - /**Returned when the requested update operation cannot be performed due to general constraints */ - BT_MESH_FOUNDATION_CANNOT_UPDATE = 0x130B, - /**Returned when the requested delete operation cannot be performed due to general constraints */ - BT_MESH_FOUNDATION_CANNOT_REMOVE = 0x130C, - /**Returned when the requested bind operation cannot be performed due to general constraints */ - BT_MESH_FOUNDATION_CANNOT_BIND = 0x130D, - /**Returned when The node cannot start advertising with Node Identity or Proxy since the maximum number of parallel advertising is reached */ - BT_MESH_FOUNDATION_TEMPORARILY_UNABLE = 0x130E, - /**Returned when the requested state cannot be set */ - BT_MESH_FOUNDATION_CANNOT_SET = 0x130F, - /**Returned when an unspecified error took place */ - BT_MESH_FOUNDATION_UNSPECIFIED = 0x1310, - /**Returned when the NetKeyIndex and AppKeyIndex combination is not valid for a Config AppKey Update */ - BT_MESH_FOUNDATION_INVALID_BINDING = 0x1311, - - // ----------------------------------------------------------------------------- - // Wi-Fi Errors - - /** Invalid firmware keyset */ - WIFI_INVALID_KEY = 0x0B01, - /** The firmware download took too long */ - WIFI_FIRMWARE_DOWNLOAD_TIMEOUT = 0x0B02, - /** Unknown request ID or wrong interface ID used */ - WIFI_UNSUPPORTED_MESSAGE_ID = 0x0B03, - /** The request is successful but some parameters have been ignored */ - WIFI_WARNING = 0x0B04, - /** No Packets waiting to be received */ - WIFI_NO_PACKET_TO_RECEIVE = 0x0B05, - /** The sleep mode is granted */ - WIFI_SLEEP_GRANTED = 0x0B08, - /** The WFx does not go back to sleep */ - WIFI_SLEEP_NOT_GRANTED = 0x0B09, - /** The SecureLink MAC key was not found */ - WIFI_SECURE_LINK_MAC_KEY_ERROR = 0x0B10, - /** The SecureLink MAC key is already installed in OTP */ - WIFI_SECURE_LINK_MAC_KEY_ALREADY_BURNED = 0x0B11, - /** The SecureLink MAC key cannot be installed in RAM */ - WIFI_SECURE_LINK_RAM_MODE_NOT_ALLOWED = 0x0B12, - /** The SecureLink MAC key installation failed */ - WIFI_SECURE_LINK_FAILED_UNKNOWN_MODE = 0x0B13, - /** SecureLink key (re)negotiation failed */ - WIFI_SECURE_LINK_EXCHANGE_FAILED = 0x0B14, - /** The device is in an inappropriate state to perform the request */ - WIFI_WRONG_STATE = 0x0B18, - /** The request failed due to regulatory limitations */ - WIFI_CHANNEL_NOT_ALLOWED = 0x0B19, - /** The connection request failed because no suitable AP was found */ - WIFI_NO_MATCHING_AP = 0x0B1A, - /** The connection request was aborted by host */ - WIFI_CONNECTION_ABORTED = 0x0B1B, - /** The connection request failed because of a timeout */ - WIFI_CONNECTION_TIMEOUT = 0x0B1C, - /** The connection request failed because the AP rejected the device */ - WIFI_CONNECTION_REJECTED_BY_AP = 0x0B1D, - /** The connection request failed because the WPA handshake did not complete successfully */ - WIFI_CONNECTION_AUTH_FAILURE = 0x0B1E, - /** The request failed because the retry limit was exceeded */ - WIFI_RETRY_EXCEEDED = 0x0B1F, - /** The request failed because the MSDU life time was exceeded */ - WIFI_TX_LIFETIME_EXCEEDED = 0x0B20, - - // ----------------------------------------------------------------------------- - // MVP Driver and MVP Math status codes - - /** Critical fault */ - COMPUTE_DRIVER_FAULT = 0x1501, - /** ALU operation output NaN */ - COMPUTE_DRIVER_ALU_NAN = 0x1502, - /** ALU numeric overflow */ - COMPUTE_DRIVER_ALU_OVERFLOW = 0x1503, - /** ALU numeric underflow */ - COMPUTE_DRIVER_ALU_UNDERFLOW = 0x1504, - /** Overflow during array store */ - COMPUTE_DRIVER_STORE_CONVERSION_OVERFLOW = 0x1505, - /** Underflow during array store conversion */ - COMPUTE_DRIVER_STORE_CONVERSION_UNDERFLOW = 0x1506, - /** Infinity encountered during array store conversion */ - COMPUTE_DRIVER_STORE_CONVERSION_INFINITY = 0x1507, - /** NaN encountered during array store conversion */ - COMPUTE_DRIVER_STORE_CONVERSION_NAN = 0x1508, - - /** MATH NaN encountered */ - COMPUTE_MATH_NAN = 0x1512, - /** MATH Infinity encountered */ - COMPUTE_MATH_INFINITY = 0x1513, - /** MATH numeric overflow */ - 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, - /** - * 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. */ - SUCCESS = 0x00, - /** Fatal error. */ - SPI_ERR_FATAL = 0x10, - /** The Response frame of the current transaction indicates the NCP has reset. */ - SPI_ERR_NCP_RESET = 0x11, - /** The NCP is reporting that the Command frame of the current transaction is oversized (the length byte is too large). */ - SPI_ERR_OVERSIZED_EZSP_FRAME = 0x12, - /** The Response frame of the current transaction indicates the previous transaction was aborted (nSSEL deasserted too soon). */ - SPI_ERR_ABORTED_TRANSACTION = 0x13, - /** The Response frame of the current transaction indicates the frame terminator is missing from the Command frame. */ - SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14, - /** The NCP has not provided a Response within the time limit defined by WAIT_SECTION_TIMEOUT. */ - SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15, - /** The Response frame from the NCP is missing the frame terminator. */ - SPI_ERR_NO_FRAME_TERMINATOR = 0x16, - /** The Host attempted to send an oversized Command (the length byte is too large) and the AVR's spi-protocol.c blocked the transmission. */ - SPI_ERR_EZSP_COMMAND_OVERSIZED = 0x17, - /** The NCP attempted to send an oversized Response (the length byte is too large) and the AVR's spi-protocol.c blocked the reception. */ - SPI_ERR_EZSP_RESPONSE_OVERSIZED = 0x18, - /** The Host has sent the Command and is still waiting for the NCP to send a Response. */ - SPI_WAITING_FOR_RESPONSE = 0x19, - /** The NCP has not asserted nHOST_INT within the time limit defined by WAKE_HANDSHAKE_TIMEOUT. */ - SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A, - /** The NCP has not asserted nHOST_INT after an NCP reset within the time limit defined by STARTUP_TIMEOUT. */ - SPI_ERR_STARTUP_TIMEOUT = 0x1B, - /** The Host attempted to verify the SPI Protocol activity and version number, and the verification failed. */ - SPI_ERR_STARTUP_FAIL = 0x1C, - /** The Host has sent a command with a SPI Byte that is unsupported by the current mode the NCP is operating in. */ - SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D, - /** Operation not yet complete. */ - ASH_IN_PROGRESS = 0x20, - /** Fatal error detected by host. */ - HOST_FATAL_ERROR = 0x21, - /** Fatal error detected by NCP. */ - ASH_NCP_FATAL_ERROR = 0x22, - /** Tried to send DATA frame too long. */ - DATA_FRAME_TOO_LONG = 0x23, - /** Tried to send DATA frame too short. */ - DATA_FRAME_TOO_SHORT = 0x24, - /** No space for tx'ed DATA frame. */ - NO_TX_SPACE = 0x25, - /** No space for rec'd DATA frame. */ - NO_RX_SPACE = 0x26, - /** No receive data available. */ - NO_RX_DATA = 0x27, - /** Not in Connected state. */ - NOT_CONNECTED = 0x28, - /** The NCP received a command before the EZSP version had been set. */ - ERROR_VERSION_NOT_SET = 0x30, - /** The NCP received a command containing an unsupported frame ID. */ - ERROR_INVALID_FRAME_ID = 0x31, - /** The direction flag in the frame control field was incorrect. */ - ERROR_WRONG_DIRECTION = 0x32, - /** - * The truncated flag in the frame control field was set, indicating there was not enough memory available to - * complete the response or that the response would have exceeded the maximum EZSP frame length. - */ - ERROR_TRUNCATED = 0x33, - /** - * The overflow flag in the frame control field was set, indicating one or more callbacks occurred since the previous - * response and there was not enough memory available to report them to the Host. - */ - ERROR_OVERFLOW = 0x34, - /** Insufficient memory was available. */ - ERROR_OUT_OF_MEMORY = 0x35, - /** The value was out of bounds. */ - ERROR_INVALID_VALUE = 0x36, - /** The configuration id was not recognized. */ - ERROR_INVALID_ID = 0x37, - /** Configuration values can no longer be modified. */ - ERROR_INVALID_CALL = 0x38, - /** The NCP failed to respond to a command. */ - ERROR_NO_RESPONSE = 0x39, - /** The length of the command exceeded the maximum EZSP frame length. */ - ERROR_COMMAND_TOO_LONG = 0x40, - /** The UART receive queue was full causing a callback response to be dropped. */ - ERROR_QUEUE_FULL = 0x41, - /** The command has been filtered out by NCP. */ - ERROR_COMMAND_FILTERED = 0x42, - /** EZSP Security Key is already set */ - ERROR_SECURITY_KEY_ALREADY_SET = 0x43, - /** EZSP Security Type is invalid */ - ERROR_SECURITY_TYPE_INVALID = 0x44, - /** EZSP Security Parameters are invalid */ - ERROR_SECURITY_PARAMETERS_INVALID = 0x45, - /** EZSP Security Parameters are already set */ - ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46, - /** EZSP Security Key is not set */ - ERROR_SECURITY_KEY_NOT_SET = 0x47, - /** EZSP Security Parameters are not set */ - ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48, - /** Received frame with unsupported control byte */ - ERROR_UNSUPPORTED_CONTROL = 0x49, - /** Received frame is unsecure, when security is established */ - ERROR_UNSECURE_FRAME = 0x4A, - /** Incompatible ASH version */ - ASH_ERROR_VERSION = 0x50, - /** Exceeded max ACK timeouts */ - ASH_ERROR_TIMEOUTS = 0x51, - /** Timed out waiting for RSTACK */ - ASH_ERROR_RESET_FAIL = 0x52, - /** Unexpected ncp reset */ - ASH_ERROR_NCP_RESET = 0x53, - /** Serial port initialization failed */ - ERROR_SERIAL_INIT = 0x54, - /** Invalid ncp processor type */ - ASH_ERROR_NCP_TYPE = 0x55, - /** Invalid ncp reset method */ - ASH_ERROR_RESET_METHOD = 0x56, - /** XON/XOFF not supported by host driver */ - ASH_ERROR_XON_XOFF = 0x57, - /** ASH protocol started */ - ASH_STARTED = 0x70, - /** ASH protocol connected */ - ASH_CONNECTED = 0x71, - /** ASH protocol disconnected */ - ASH_DISCONNECTED = 0x72, - /** Timer expired waiting for ack */ - ASH_ACK_TIMEOUT = 0x73, - /** Frame in progress cancelled */ - ASH_CANCELLED = 0x74, - /** Received frame out of sequence */ - ASH_OUT_OF_SEQUENCE = 0x75, - /** Received frame with CRC error */ - ASH_BAD_CRC = 0x76, - /** Received frame with comm error */ - ASH_COMM_ERROR = 0x77, - /** Received frame with bad ackNum */ - ASH_BAD_ACKNUM = 0x78, - /** Received frame shorter than minimum */ - ASH_TOO_SHORT = 0x79, - /** Received frame longer than maximum */ - ASH_TOO_LONG = 0x7A, - /** Received frame with illegal control byte */ - ASH_BAD_CONTROL = 0x7B, - /** Received frame with illegal length for its type */ - ASH_BAD_LENGTH = 0x7C, - /** Received ASH Ack */ - ASH_ACK_RECEIVED = 0x7D, - /** Sent ASH Ack */ - ASH_ACK_SENT = 0x7E, - /** Received ASH Nak */ - ASH_NAK_RECEIVED = 0x7F, - /** Sent ASH Nak */ - ASH_NAK_SENT = 0x80, - /** Received ASH RST */ - ASH_RST_RECEIVED = 0x81, - /** Sent ASH RST */ - ASH_RST_SENT = 0x82, - /** ASH Status */ - ASH_STATUS = 0x83, - /** ASH TX */ - ASH_TX = 0x84, - /** ASH RX */ - ASH_RX = 0x85, - /** Failed to connect to CPC daemon or failed to open CPC endpoint */ - CPC_ERROR_INIT = 0x86, - /** No reset or error */ - NO_ERROR = 0xFF -}; - -export enum EmberStackError { - // Error codes that a router uses to notify the message initiator about a broken route. - ROUTE_ERROR_NO_ROUTE_AVAILABLE = 0x00, - ROUTE_ERROR_TREE_LINK_FAILURE = 0x01, - ROUTE_ERROR_NON_TREE_LINK_FAILURE = 0x02, - ROUTE_ERROR_LOW_BATTERY_LEVEL = 0x03, - ROUTE_ERROR_NO_ROUTING_CAPACITY = 0x04, - ROUTE_ERROR_NO_INDIRECT_CAPACITY = 0x05, - ROUTE_ERROR_INDIRECT_TRANSACTION_EXPIRY = 0x06, - ROUTE_ERROR_TARGET_DEVICE_UNAVAILABLE = 0x07, - ROUTE_ERROR_TARGET_ADDRESS_UNALLOCATED = 0x08, - ROUTE_ERROR_PARENT_LINK_FAILURE = 0x09, - ROUTE_ERROR_VALIDATE_ROUTE = 0x0A, - ROUTE_ERROR_SOURCE_ROUTE_FAILURE = 0x0B, - ROUTE_ERROR_MANY_TO_ONE_ROUTE_FAILURE = 0x0C, - ROUTE_ERROR_ADDRESS_CONFLICT = 0x0D, - ROUTE_ERROR_VERIFY_ADDRESSES = 0x0E, - ROUTE_ERROR_PAN_IDENTIFIER_UPDATE = 0x0F, - - NETWORK_STATUS_NETWORK_ADDRESS_UPDATE = 0x10, - NETWORK_STATUS_BAD_FRAME_COUNTER = 0x11, - NETWORK_STATUS_BAD_KEY_SEQUENCE_NUMBER = 0x12, - NETWORK_STATUS_UNKNOWN_COMMAND = 0x13 -} - -/** Type of Ember software version */ -export enum EmberVersionType { - PRE_RELEASE = 0x00, - - // Alpha, should be used rarely - ALPHA_1 = 0x11, - ALPHA_2 = 0x12, - ALPHA_3 = 0x13, - // Leave space in case we decide to add other types in the future. - BETA_1 = 0x21, - BETA_2 = 0x22, - BETA_3 = 0x23, - - // Anything other than 0xAA is considered pre-release - // Silicon Labs may define other types in the future (e.g. beta, alpha) - // Silicon Labs chose an arbitrary number (0xAA) to allow for expansion, but - // to prevent ambiguity in case 0x00 or 0xFF is accidentally retrieved - // as the version type. - 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 - */ -export enum EmberTXPowerMode { - /** - * The application should call ::emberSetTxPowerMode() with the - * txPowerMode parameter set to this value to disable all power mode options, - * resulting in normal power mode and bi-directional RF transmitter output. - */ - DEFAULT = 0x0000, - /** - * The application should call ::emberSetTxPowerMode() with the - * txPowerMode parameter set to this value to enable boost power mode. - */ - BOOST = 0x0001, - /** - * The application should call ::emberSetTxPowerMode() with the - * txPowerMode parameter set to this value to enable the alternate transmitter - * output. - */ - ALTERNATE = 0x0002, - /** - * The application should call ::emberSetTxPowerMode() with the - * txPowerMode parameter set to this value to enable both boost mode and the - * alternate transmitter output. - */ - BOOST_AND_ALTERNATE = 0x0003,// (BOOST | ALTERNATE) - // The application does not ever need to call emberSetTxPowerMode() with the - // txPowerMode parameter set to this value. This value is used internally by - // the stack to indicate that the default token configuration has not been - // overridden by a prior call to emberSetTxPowerMode(). - USE_TOKEN = 0x8000, -} - -/** uint8_t */ -export enum EmberKeepAliveMode { - KEEP_ALIVE_SUPPORT_UNKNOWN = 0x00, - MAC_DATA_POLL_KEEP_ALIVE = 0x01, - END_DEVICE_TIMEOUT_KEEP_ALIVE = 0x02, - KEEP_ALIVE_SUPPORT_ALL = 0x03, -}; - -/** This is the Extended Security Bitmask that controls the use of various extended security features. */ -export enum EmberExtendedSecurityBitmask { - /** - * If this bit is set, the 'key token data' field is set in the Initial Security Bitmask to 0 (No Preconfig Key token). - * Otherwise, the field is left as is. - */ - PRECONFIG_KEY_NOT_VALID = 0x0001, - // bits 2-3 are unused. - /** - * This denotes that the network key update can only happen if the network key update request is unicast and encrypted - * i.e. broadcast network key update requests will not be processed if bit 1 is set - */ - SECURE_NETWORK_KEY_ROTATION = 0x0002, - /** This denotes whether a joiner node (router or end-device) uses a Global Link Key or a Unique Link Key. */ - JOINER_GLOBAL_LINK_KEY = 0x0010, - /** - * This denotes whether the device's outgoing frame counter is allowed to be reset during forming or joining. - * If the flag is set, the outgoing frame counter is not allowed to be reset. - * If the flag is not set, the frame counter is allowed to be reset. - */ - EXT_NO_FRAME_COUNTER_RESET = 0x0020, - /** This denotes whether a device should discard or accept network leave without rejoin commands. */ - NWK_LEAVE_WITHOUT_REJOIN_NOT_ALLOWED = 0x0040, - // Bit 7 reserved for future use (stored in TOKEN). - /** This denotes whether a router node should discard or accept network Leave Commands. */ - NWK_LEAVE_REQUEST_NOT_ALLOWED = 0x0100, - /** - * This denotes whether a node is running the latest stack specification or is emulating R18 specs behavior. - * If this flag is enabled, a router node should only send encrypted Update Device messages while the TC - * should only accept encrypted Updated Device messages. - */ - R18_STACK_BEHAVIOR = 0x0200, - // Bit 10 is reserved for future use (stored in TOKEN). - // Bit 11 is reserved for future use(stored in RAM). - // Bit 12 - This denotes whether an end device should discard or accept ZDO Leave - // from a network node other than its parent. - ZDO_LEAVE_FROM_NON_PARENT_NOT_ALLOWED = 0x1000, - // Bits 13-15 are unused. -}; - -/** This is the Initial Security Bitmask that controls the use of various security features. */ -export enum EmberInitialSecurityBitmask { - /** Enables Distributed Trust Center Mode for the device forming the network. (Previously known as ::EMBER_NO_TRUST_CENTER_MODE) */ - DISTRIBUTED_TRUST_CENTER_MODE = 0x0002, - /** Enables a Global Link Key for the Trust Center. All nodes will share the same Trust Center Link Key. */ - TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004, - /** Enables devices that perform MAC Association with a pre-configured Network Key to join the network. It is only set on the Trust Center. */ - PRECONFIGURED_NETWORK_KEY_MODE = 0x0008, - // Hidden field used internally. - HAVE_TRUST_CENTER_UNKNOWN_KEY_TOKEN = 0x0010, - // Hidden field used internally. - HAVE_TRUST_CENTER_LINK_KEY_TOKEN = 0x0020, - /** - * This denotes that the ::EmberInitialSecurityState::preconfiguredTrustCenterEui64 has a value in it containing the trust center EUI64. - * The device will only join a network and accept commands from a trust center with that EUI64. - * Normally this bit is NOT set and the EUI64 of the trust center is learned during the join process. - * When commissioning a device to join onto an existing network that is using a trust center and without sending any messages, - * this bit must be set and the field ::EmberInitialSecurityState::preconfiguredTrustCenterEui64 must be populated with the appropriate EUI64. - */ - HAVE_TRUST_CENTER_EUI64 = 0x0040, - /** - * This denotes that the ::EmberInitialSecurityState::preconfiguredKey is not the actual Link Key but a Root Key known only to the Trust Center. - * It is hashed with the IEEE Address of the destination device to create the actual Link Key used in encryption. - * This is bit is only used by the Trust Center. The joining device need not set this. - */ - TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084, - /** - * This denotes that the ::EmberInitialSecurityState::preconfiguredKey element has valid data that should be used to configure - * the initial security state. - */ - HAVE_PRECONFIGURED_KEY = 0x0100, - /** - * This denotes that the ::EmberInitialSecurityState::networkKey element has valid data that should be used to configure - * the initial security state. - */ - HAVE_NETWORK_KEY = 0x0200, - /** - * This denotes to a joining node that it should attempt to acquire a Trust Center Link Key during joining. - * This is necessary if the device does not have a pre-configured key, or wants to obtain a new one - * (since it may be using a well-known key during joining). - */ - GET_LINK_KEY_WHEN_JOINING = 0x0400, - /** - * This denotes that a joining device should only accept an encrypted network key from the Trust Center (using its pre-configured key). - * A key sent in-the-clear by the Trust Center will be rejected and the join will fail. - * This option is only valid when using a pre-configured key. - */ - REQUIRE_ENCRYPTED_KEY = 0x0800, - /** - * This denotes whether the device should NOT reset its outgoing frame counters (both NWK and APS) when - * ::emberSetInitialSecurityState() is called. - * Normally it is advised to reset the frame counter before joining a new network. - * However, when a device is joining to the same network again (but not using ::emberRejoinNetwork()), - * it should keep the NWK and APS frame counters stored in its tokens. - * - * NOTE: The application is allowed to dynamically change the behavior via EMBER_EXT_NO_FRAME_COUNTER_RESET field. - */ - NO_FRAME_COUNTER_RESET = 0x1000, - /** - * This denotes that the device should obtain its pre-configured key from an installation code stored in the manufacturing token. - * The token contains a value that will be hashed to obtain the actual pre-configured key. - * If that token is not valid, the call to ::emberSetInitialSecurityState() will fail. - */ - GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000, - // Internal data - EM_SAVED_IN_TOKEN = 0x4000, - /* All other bits are reserved and must be zero. */ -} - -/** Either marks an event as inactive or specifies the units for the event execution time. uint8_t */ -export enum EmberEventUnits { - /** The event is not scheduled to run. */ - INACTIVE = 0, - /** The execution time is in approximate milliseconds. */ - MS_TIME = 1, - /** The execution time is in 'binary' quarter seconds (256 approximate milliseconds each). */ - QS_TIME = 2, - /** The execution time is in 'binary' minutes (65536 approximate milliseconds each). */ - MINUTE_TIME = 3, - /** The event is scheduled to run at the earliest opportunity. */ - ZERO_DELAY = 4, -}; - -/** - * Defines the events reported to the application - * by the ::emberCounterHandler(). Usage of the destinationNodeId - * or data fields found in the EmberCounterInfo or EmberExtraCounterInfo - * structs is denoted for counter types that use them. (See comments - * accompanying enum definitions in this source file for details.) - */ -export enum EmberCounterType { - /** The MAC received a broadcast Data frame, Command frame, or Beacon - * destinationNodeId: BROADCAST_ADDRESS or Data frames or - * sender node ID for Beacon frames - * data: not used - */ - MAC_RX_BROADCAST = 0, - /** The MAC transmitted a broadcast Data frame, Command frame or Beacon. - * destinationNodeId: BROADCAST_ADDRESS - * data: not used - */ - MAC_TX_BROADCAST = 1, - /** The MAC received a unicast Data or Command frame - * destinationNodeId: MAC layer source or EMBER_UNKNOWN_NODE_ID - * if no 16-bit source node ID is present in the frame - * data: not used - */ - MAC_RX_UNICAST = 2, - /** The MAC successfully transmitted a unicast Data or Command frame - * Note: Only frames with a 16-bit destination node ID are counted. - * destinationNodeId: MAC layer destination address - * data: not used - */ - MAC_TX_UNICAST_SUCCESS = 3, - /** The MAC retried a unicast Data or Command frame after initial Tx attempt. - * Note: CSMA-related failures are tracked separately via - * PHY_CCA_FAIL_COUNT. - * destinationNodeId: MAC layer destination or EMBER_UNKNOWN_NODE_ID - * if no 16-bit destination node ID is present in the frame - * data: number of retries (after initial Tx attempt) accumulated so far - * for this packet. (Should always be >0.) - */ - MAC_TX_UNICAST_RETRY = 4, - /** The MAC unsuccessfully transmitted a unicast Data or Command frame. - * Note: Only frames with a 16-bit destination node ID are counted. - * destinationNodeId: MAC layer destination address - * data: not used - */ - MAC_TX_UNICAST_FAILED = 5, - /** The APS layer received a data broadcast. - * destinationNodeId: sender's node ID - * data: not used - */ - APS_DATA_RX_BROADCAST = 6, - /** The APS layer transmitted a data broadcast. */ - APS_DATA_TX_BROADCAST = 7, - /** The APS layer received a data unicast. - * destinationNodeId: sender's node ID - * data: not used - */ - APS_DATA_RX_UNICAST = 8, - /** The APS layer successfully transmitted a data unicast. - * destinationNodeId: NWK destination address - * data: number of APS retries (>=0) consumed for this unicast. - */ - APS_DATA_TX_UNICAST_SUCCESS = 9, - /** The APS layer retried a unicast Data frame. This is a placeholder - * and is not used by the @c ::emberCounterHandler() callback. Instead, - * the number of APS retries are returned in the data parameter - * of the callback for the @c ::APS_DATA_TX_UNICAST_SUCCESS - * and @c ::APS_DATA_TX_UNICAST_FAILED types. - * However, our supplied Counters component code will attempt to collect this - * information from the aforementioned counters and populate this counter. - * Note that this counter's behavior differs from that of - * @c ::MAC_TX_UNICAST_RETRY . - */ - APS_DATA_TX_UNICAST_RETRY = 10, - /* The APS layer unsuccessfully transmitted a data unicast. - * destinationNodeId: NWK destination address - * data: number of APS retries (>=0) consumed for this unicast. - */ - APS_DATA_TX_UNICAST_FAILED = 11, - /** The network layer successfully submitted a new route discovery - * to the MAC. */ - ROUTE_DISCOVERY_INITIATED = 12, - /** An entry was added to the neighbor table. */ - NEIGHBOR_ADDED = 13, - /** An entry was removed from the neighbor table. */ - NEIGHBOR_REMOVED = 14, - /** A neighbor table entry became stale because it had not been heard from. */ - NEIGHBOR_STALE = 15, - /** A node joined or rejoined to the network via this node. - * destinationNodeId: node ID of child - * data: not used - */ - JOIN_INDICATION = 16, - /** An entry was removed from the child table. - * destinationNodeId: node ID of child - * data: not used - */ - CHILD_REMOVED = 17, - /** EZSP-UART only. An overflow error occurred in the UART. */ - ASH_OVERFLOW_ERROR = 18, - /** EZSP-UART only. A framing error occurred in the UART. */ - ASH_FRAMING_ERROR = 19, - /** EZSP-UART only. An overrun error occurred in the UART. */ - ASH_OVERRUN_ERROR = 20, - /** A message was dropped at the Network layer because the NWK frame - counter was not higher than the last message seen from that source. */ - NWK_FRAME_COUNTER_FAILURE = 21, - /** A message was dropped at the APS layer because the APS frame counter - was not higher than the last message seen from that source. - destinationNodeId: node ID of MAC source that relayed the message - data: not used - */ - APS_FRAME_COUNTER_FAILURE = 22, - /** EZSP-UART only. An XOFF was transmitted by the UART. */ - ASH_XOFF = 23, - /** An encrypted message was dropped by the APS layer because - * the sender's key has not been authenticated. - * As a result, the key is not authorized for use in APS data messages. - * destinationNodeId: EMBER_NULL_NODE_ID - * data: APS key table index related to the sender - */ - APS_LINK_KEY_NOT_AUTHORIZED = 24, - /** A NWK encrypted message was received but dropped because decryption - * failed. - * destinationNodeId: sender of the dropped packet - * data: not used - */ - NWK_DECRYPTION_FAILURE = 25, - /** An APS encrypted message was received but dropped because decryption - * failed. - * destinationNodeId: sender of the dropped packet - * data: not used - */ - APS_DECRYPTION_FAILURE = 26, - /** The number of failures to allocate a set of linked packet buffers. - This doesn't necessarily mean that the packet buffer count was - 0 at the time, but that the number requested was greater than the - number free. */ - ALLOCATE_PACKET_BUFFER_FAILURE = 27, - /** The number of relayed unicast packets. - destinationId: NWK layer destination address of relayed packet - data: not used - */ - RELAYED_UNICAST = 28, - /** The number of times a packet was dropped due to reaching the preset - * PHY-to-MAC queue limit (sli_mac_phy_to_mac_queue_length). The limit will - * determine how many messages are accepted by the PHY between calls to - * emberTick(). After that limit is reached, packets will be dropped. - * The counter records the number of dropped packets. - * - * NOTE: For each call to emberCounterHandler() there may be more - * than 1 packet that was dropped due to the limit reached. The - * actual number of packets dropped will be returned in the 'data' - * parameter passed to that function. - * - * destinationNodeId: not used - * data: number of dropped packets represented by this counter event - * phyIndex: present - */ - PHY_TO_MAC_QUEUE_LIMIT_REACHED = 29, - /** The number of times a packet was dropped due to the packet-validate - library checking a packet and rejecting it due to length or - other formatting problems. - destinationNodeId: not used - data: type of validation condition that failed - */ - PACKET_VALIDATE_LIBRARY_DROPPED_COUNT = 30, - /** The number of times the NWK retry queue is full and a new - message failed to be added. - destinationNodeId; not used - data: NWK retry queue size that has been exceeded - */ - TYPE_NWK_RETRY_OVERFLOW = 31, - /** The number of times the PHY layer was unable to transmit due to - * a failed CCA (Clear Channel Assessment) attempt. See also: - * MAC_TX_UNICAST_RETRY. - * destinationNodeId: MAC layer destination or EMBER_UNKNOWN_NODE_ID - * if no 16-bit destination node ID is present in the frame - * data: not used - */ - PHY_CCA_FAIL_COUNT = 32, - /** The number of times a NWK broadcast was dropped because - the broadcast table was full. - */ - BROADCAST_TABLE_FULL = 33, - /** The number of times a low-priority packet traffic arbitration - request has been made. - */ - PTA_LO_PRI_REQUESTED = 34, - /** The number of times a high-priority packet traffic arbitration - request has been made. - */ - PTA_HI_PRI_REQUESTED = 35, - /** The number of times a low-priority packet traffic arbitration - request has been denied. - */ - PTA_LO_PRI_DENIED = 36, - /** The number of times a high-priority packet traffic arbitration - request has been denied. - */ - PTA_HI_PRI_DENIED = 37, - /** The number of times a low-priority packet traffic arbitration - transmission has been aborted. - */ - PTA_LO_PRI_TX_ABORTED = 38, - /** The number of times a high-priority packet traffic arbitration - transmission has been aborted. - */ - PTA_HI_PRI_TX_ABORTED = 39, - /** The number of times an address conflict has caused node_id change, and an address conflict error is sent - */ - ADDRESS_CONFLICT_SENT = 40, - /** A placeholder giving the number of Ember counter types. */ - COUNT = 41, -}; - -/* eslint-disable @typescript-eslint/no-duplicate-enum-values */ -/** An enumerated list of library identifiers. */ -export enum EmberLibraryId { - FIRST = 0x00, - - ZIGBEE_PRO = 0x00, - BINDING = 0x01, - END_DEVICE_BIND = 0x02, - SECURITY_CORE = 0x03, - SECURITY_LINK_KEYS = 0x04, - ALARM = 0x05, - CBKE = 0x06, - CBKE_DSA_SIGN = 0x07, - ECC = 0x08, - CBKE_DSA_VERIFY = 0x09, - PACKET_VALIDATE = 0x0A, - INSTALL_CODE = 0x0B, - ZLL = 0x0C, - CBKE_283K1 = 0x0D, - ECC_283K1 = 0x0E, - CBKE_CORE = 0x0F, - NCP = 0x10, - MULTI_NETWORK = 0x11, - ENHANCED_BEACON_REQUEST = 0x12, - CBKE_283K1_DSA_VERIFY = 0x13, - MULTI_PAN = 0x14, - - NUMBER_OF_LIBRARIES = 0x15, - NULL = 0xFF -}; - -/** This indicates the presence, absence, or status of an Ember stack library. */ -export enum EmberLibraryStatus { - // Base return codes. These may be ORed with statuses further below. - LIBRARY_PRESENT_MASK = 0x80, - LIBRARY_IS_STUB = 0x00, - LIBRARY_ERROR = 0xFF, - - // The ZigBee Pro library uses the following to indicate additional functionality: - /** no router capability */ - ZIGBEE_PRO_LIBRARY_END_DEVICE_ONLY = 0x00, - ZIGBEE_PRO_LIBRARY_HAVE_ROUTER_CAPABILITY = 0x01, - ZIGBEE_PRO_LIBRARY_ZLL_SUPPORT = 0x02, - - // The Security library uses the following to indicate additional functionality: - SECURITY_LIBRARY_END_DEVICE_ONLY = 0x00, - /** router or trust center support */ - SECURITY_LIBRARY_HAVE_ROUTER_SUPPORT = 0x01, - - // The Packet Validate library may be globally turned on/off. Bit 0 indicates whether the library is enabled/disabled. - PACKET_VALIDATE_LIBRARY_DISABLED = 0x00, - PACKET_VALIDATE_LIBRARY_ENABLED = 0x01, - PACKET_VALIDATE_LIBRARY_ENABLE_MASK = 0x01 -}; -/* eslint-enable @typescript-eslint/no-duplicate-enum-values */ - -/** Defines the entropy source used by the stack. */ -export enum EmberEntropySource { - /** Error in identifying the entropy source. */ - ERROR = 0x00, - /** The default radio entropy source. */ - RADIO = 0x01, - /** TRNG with mbed TLS support. */ - MBEDTLS_TRNG = 0x02, - /** Other mbed TLS entropy source. */ - MBEDTLS = 0x03, -}; - -/** Defines the options that should be used when initializing the node's network configuration. */ -export enum EmberNetworkInitBitmask { - NO_OPTIONS = 0x0000, - /** The Parent Node ID and EUI64 are stored in a token. This prevents the need to perform an Orphan scan on startup. */ - PARENT_INFO_IN_TOKEN = 0x0001, - /** Z3 compliant end devices on a network must send a rejoin request on reboot. */ - END_DEVICE_REJOIN_ON_REBOOT = 0x0002, -}; - -/** Defines the possible join states for a node. uint8_t */ -export enum EmberNetworkStatus { - /** The node is not associated with a network in any way. */ - NO_NETWORK, - /** The node is currently attempting to join a network. */ - JOINING_NETWORK, - /** The node is joined to a network. */ - JOINED_NETWORK, - /** The node is an end device joined to a network but its parent is not responding. */ - JOINED_NETWORK_NO_PARENT, - /** The node is a Sleepy-to-Sleepy initiator */ - JOINED_NETWORK_S2S_INITIATOR, - /** The node is a Sleepy-to-Sleepy target */ - JOINED_NETWORK_S2S_TARGET, - /** The node is in the process of leaving its current network. */ - LEAVING_NETWORK -}; - -/** Network scan types. */ -export enum EzspNetworkScanType { - /** An energy scan scans each channel for its RSSI value. */ - ENERGY_SCAN = 0x00, - /** An active scan scans each channel for available networks. */ - ACTIVE_SCAN = 0x01 -}; - -/** The type of method used for joining. uint8_t */ -export enum EmberJoinMethod { - /** Devices normally use MAC association to join a network, which respects - * the "permit joining" flag in the MAC beacon. - * This value should be used by default. - */ - MAC_ASSOCIATION = 0, - /** For networks where the "permit joining" flag is never turned - * on, devices will need to use a ZigBee NWK Rejoin. This value causes the - * rejoin to be sent withOUT NWK security and the Trust Center will be - * asked to send the NWK key to the device. The NWK key sent to the device - * can be encrypted with the device's corresponding Trust Center link key. - * That is determined by the ::EmberJoinDecision on the Trust Center - * returned by the ::emberTrustCenterJoinHandler(). - */ - NWK_REJOIN = 1, - /* For networks where the "permit joining" flag is never turned - * on, devices will need to use a NWK Rejoin. If those devices have been - * preconfigured with the NWK key (including sequence number), they can use - * a secured rejoin. This is only necessary for end devices since they need - * a parent. Routers can simply use the ::CONFIGURED_NWK_STATE - * join method below. - */ - NWK_REJOIN_HAVE_NWK_KEY = 2, - /** For networks where all network and security information is known - ahead of time, a router device may be commissioned such that it does - not need to send any messages to begin communicating on the network. - */ - CONFIGURED_NWK_STATE = 3, - /** This enumeration causes an unencrypted Network Commissioning Request to be - sent out with joinType set to initial join. The trust center may respond - by establishing a new dynamic link key and then sending the network key. - Network Commissioning Requests should only be sent to parents that support - processing of the command. - */ - NWK_COMMISSIONING_JOIN = 4, - /** This enumeration causes an unencrypted Network Commissioning Request to be - sent out with joinType set to rejoin. The trust center may respond - by establishing a new dynamic link key and then sending the network key. - Network Commissioning Requests should only be sent to parents that support - processing of the command. - */ - NWK_COMMISSIONING_REJOIN = 5, - /** This enumeration causes an encrypted Network Commissioning Request to be - sent out with joinType set to rejoin. This enumeration is used by devices - that already have the network key and wish to recover connection to a - parent or the network in general. - Network Commissioning Requests should only be sent to parents that support - processing of the command. - */ - NWK_COMMISSIONING_REJOIN_HAVE_NWK_KEY = 6, -}; - -/** Defines the possible types of nodes and the roles that a node might play in a network. */ -export enum EmberNodeType { - /** The device is not joined. */ - UNKNOWN_DEVICE = 0, - /** Will relay messages and can act as a parent to other nodes. */ - COORDINATOR = 1, - /** Will relay messages and can act as a parent to other nodes. */ - ROUTER = 2, - /** Communicates only with its parent and will not relay messages. */ - END_DEVICE = 3, - /** An end device whose radio can be turned off to save power. The application must call ::emberPollForData() to receive messages. */ - SLEEPY_END_DEVICE = 4, - /** Sleepy end device which transmits with wake up frames (CSL). */ - S2S_INITIATOR_DEVICE = 5, - /** Sleepy end device which duty cycles the radio Rx (CSL). */ - S2S_TARGET_DEVICE = 6, -}; - -/** */ -export enum EmberMultiPhyNwkConfig { - ROUTERS_ALLOWED = 0x01, - BROADCASTS_ENABLED = 0x02, - DISABLED = 0x80 -}; - -/** - * Duty cycle states - * - * Applications have no control over the state but the callback exposes - * state changes to the application. - */ -export enum EmberDutyCycleState { - /** No duty cycle tracking or metrics are taking place. */ - TRACKING_OFF = 0, - /** Duty Cycle is tracked and has not exceeded any thresholds. */ - LBT_NORMAL = 1, - /** The limited threshold of the total duty cycle allotment was exceeded. */ - LBT_LIMITED_THRESHOLD_REACHED = 2, - /** The critical threshold of the total duty cycle allotment was exceeded. */ - LBT_CRITICAL_THRESHOLD_REACHED = 3, - /** The suspend limit was reached and all outbound transmissions are blocked. */ - LBT_SUSPEND_LIMIT_REACHED = 4, -}; - -/** Defines binding types. uint8_t */ -export enum EmberBindingType { - /** A binding that is currently not in use. */ - UNUSED_BINDING = 0, - /** A unicast binding whose 64-bit identifier is the destination EUI64. */ - UNICAST_BINDING = 1, - /** A unicast binding whose 64-bit identifier is the many-to-one - * destination EUI64. Route discovery should be disabled when sending - * unicasts via many-to-one bindings. */ - MANY_TO_ONE_BINDING = 2, - /** A multicast binding whose 64-bit identifier is the group address. This - * binding can be used to send messages to the group and to receive - * messages sent to the group. */ - MULTICAST_BINDING = 3, -}; - -/** Defines the possible outgoing message types. uint8_t */ -export enum EmberOutgoingMessageType { - /** Unicast sent directly to an EmberNodeId. */ - DIRECT, - /** Unicast sent using an entry in the address table. */ - VIA_ADDRESS_TABLE, - /** Unicast sent using an entry in the binding table. */ - VIA_BINDING, - /** Multicast message. This value is passed to emberMessageSentHandler() only. - * It may not be passed to emberSendUnicast(). */ - MULTICAST, - /** An aliased multicast message. This value is passed to emberMessageSentHandler() only. - * It may not be passed to emberSendUnicast(). */ - MULTICAST_WITH_ALIAS, - /** An aliased Broadcast message. This value is passed to emberMessageSentHandler() only. - * It may not be passed to emberSendUnicast(). */ - BROADCAST_WITH_ALIAS, - /** A broadcast message. This value is passed to emberMessageSentHandler() only. - * It may not be passed to emberSendUnicast(). */ - BROADCAST -}; - -/** Defines the possible incoming message types. uint8_t */ -export enum EmberIncomingMessageType { - /** Unicast. */ - UNICAST, - /** Unicast reply. */ - UNICAST_REPLY, - /** Multicast. */ - MULTICAST, - /** Multicast sent by the local device. */ - MULTICAST_LOOPBACK, - /** Broadcast. */ - BROADCAST, - /** Broadcast sent by the local device. */ - BROADCAST_LOOPBACK -}; - -/** - * Options to use when sending a message. - * - * The discover-route, APS-retry, and APS-indirect options may be used together. - * Poll response cannot be combined with any other options. - * uint16_t - */ -export enum EmberApsOption { - /** No options. */ - NONE = 0x0000, - ENCRYPT_WITH_TRANSIENT_KEY = 0x0001, - USE_ALIAS_SEQUENCE_NUMBER = 0x0002, - /** - * This signs the application layer message body (APS Frame not included) and appends the ECDSA signature to the end of the message, - * which is needed by Smart Energy applications and requires the CBKE and ECC libraries. - * The ::emberDsaSignHandler() function is called after DSA signing is complete but before the message has been sent by the APS layer. - * Note that when passing a buffer to the stack for DSA signing, the final byte in the buffer has a special significance as an indicator - * of how many leading bytes should be ignored for signature purposes. See the API documentation of emberDsaSign() - * or the dsaSign EZSP command for more details about this requirement. - */ - DSA_SIGN = 0x0010, - /** Send the message using APS Encryption using the Link Key shared with the destination node to encrypt the data at the APS Level. */ - ENCRYPTION = 0x0020, - /** - * Resend the message using the APS retry mechanism. - * This option and the enable route discovery option must be enabled for an existing route to be repaired automatically. - */ - RETRY = 0x0040, - /** - * Send the message with the NWK 'enable route discovery' flag, which causes a route discovery to be initiated if no route to the - * destination is known. Note that in the mesh stack, this option and the APS retry option must be enabled an existing route to be - * repaired automatically. - */ - ENABLE_ROUTE_DISCOVERY = 0x0100, - /** Send the message with the NWK 'force route discovery' flag, which causes a route discovery to be initiated even if one is known. */ - FORCE_ROUTE_DISCOVERY = 0x0200, - /** Include the source EUI64 in the network frame. */ - SOURCE_EUI64 = 0x0400, - /** Include the destination EUI64 in the network frame. */ - DESTINATION_EUI64 = 0x0800, - /** Send a ZDO request to discover the node ID of the destination if it is not already known. */ - ENABLE_ADDRESS_DISCOVERY = 0x1000, - /** - * This message is being sent in response to a call to ::emberPollHandler(). - * It causes the message to be sent immediately instead of being queued up until the next poll from the (end device) destination. - */ - POLL_RESPONSE = 0x2000, - /** - * This incoming message is a valid ZDO request and the application is responsible for sending a ZDO response. - * This flag is used only within emberIncomingMessageHandler() when EMBER_APPLICATION_RECEIVES_UNSUPPORTED_ZDO_REQUESTS is defined. */ - ZDO_RESPONSE_REQUIRED = 0x4000, - /** - * This message is part of a fragmented message. This option may only be set for unicasts. - * The groupId field gives the index of this fragment in the low-order byte. - * If the low-order byte is zero this is the first fragment and the high-order byte contains the number of fragments in the message. - */ - FRAGMENT = 0x8000,// SIGNED_ENUM 0x8000 -}; - -/** - * Types of source route discovery modes used by the concentrator. - * - * OFF no source route discovery is scheduled - * - * ON source routes discovery is scheduled, and it is triggered periodically - * - * RESCHEDULE source routes discoveries are re-scheduled to be sent once immediately and then triggered periodically - */ -export enum EmberSourceRouteDiscoveryMode { - /** off */ - OFF = 0x00, - /** on */ - ON = 0x01, - /** reschedule */ - RESCHEDULE = 0x02, -}; - -/** The types of MAC passthrough messages that an application may receive. This is a bitmask. */ -export enum EmberMacPassthroughType { - /** No MAC passthrough messages. */ - NONE = 0x00, - /** SE InterPAN messages. */ - SE_INTERPAN = 0x01, - /** EmberNet and first generation (v1) standalone bootloader messages. */ - EMBERNET = 0x02, - /** EmberNet messages filtered by their source address. */ - EMBERNET_SOURCE = 0x04, - /** Application-specific passthrough messages. */ - APPLICATION = 0x08, - /** Custom inter-pan filter. */ - CUSTOM = 0x10, - - /** Internal Stack passthrough. */ - INTERNAL_ZLL = 0x80, - INTERNAL_GP = 0x40 -}; - -/** - * Interpan Message type: unicast, broadcast, or multicast. - * uint8_t - */ -export enum EmberInterpanMessageType { - UNICAST = 0x00, - BROADCAST = 0x08, - MULTICAST = 0x0C, -} - -/** This is the Current Security Bitmask that details the use of various security features. */ -export enum EmberCurrentSecurityBitmask { - // These options are the same for Initial and Current Security state. - - /** This denotes that the device is running in a network with ZigBee - * Standard Security. */ - STANDARD_SECURITY_MODE_ = 0x0000, - /** This denotes that the device is running in a network without - * a centralized Trust Center. */ - DISTRIBUTED_TRUST_CENTER_MODE_ = 0x0002, - /** This denotes that the device has a Global Link Key. The Trust Center - * Link Key is the same across multiple nodes. */ - TRUST_CENTER_GLOBAL_LINK_KEY_ = 0x0004, - - // Bit 3 reserved - - /** This denotes that the node has a Trust Center Link Key. */ - HAVE_TRUST_CENTER_LINK_KEY = 0x0010, - - /** This denotes that the Trust Center is using a Hashed Link Key. */ - TRUST_CENTER_USES_HASHED_LINK_KEY_ = 0x0084, - - // Bits 1, 5, 6, and 8-15 reserved. -}; - -/** - * The list of supported key types used by Zigbee Security Manager. - * uint8_t - */ -export enum SecManKeyType { - NONE, - /** - * This is the network key, used for encrypting and decrypting network payloads. - * There is only one of these keys in storage. - */ - NETWORK, - /** - * This is the Trust Center Link Key. On the joining device, this is the APS - * key used to communicate with the trust center. On the trust center, this - * key can be used as a root key for APS encryption and decryption when - * communicating with joining devices (if the security policy has the - * EMBER_TRUST_CENTER_USES_HASHED_LINK_KEY bit set). - * There is only one of these keys in storage. - */ - TC_LINK, - /** - * This is a Trust Center Link Key, but it times out after either - * ::EMBER_TRANSIENT_KEY_TIMEOUT_S or - * ::EMBER_AF_PLUGIN_NETWORK_CREATOR_SECURITY_NETWORK_OPEN_TIME_S (if - * defined), whichever is longer. This type of key is set on trust centers - * who wish to open joining with a temporary, or transient, APS key for - * devices to join with. Joiners who wish to try several keys when joining a - * network may set several of these types of keys before attempting to join. - * This is an indexed key, and local storage can fit as many keys as - * available RAM allows. - */ - TC_LINK_WITH_TIMEOUT, - /** - * This is an Application link key. On both joining devices and the trust - * center, this key is used in APS encryption and decryption when - * communicating to a joining device. - * This is an indexed key table of size EMBER_KEY_TABLE_SIZE, so long as there - * is sufficient nonvolatile memory to store keys. - */ - APP_LINK, - /** This is the ZLL encryption key for use by algorithms that require it. */ - ZLL_ENCRYPTION_KEY, - /** For ZLL, this is the pre-configured link key used during classical ZigBee commissioning. */ - ZLL_PRECONFIGURED_KEY, - /** This is a Green Power Device (GPD) key used on a Proxy device. */ - GREEN_POWER_PROXY_TABLE_KEY, - /** This is a Green Power Device (GPD) key used on a Sink device. */ - GREEN_POWER_SINK_TABLE_KEY, - /** - * This is a generic key type intended to be loaded for one-time hashing or crypto operations. - * This key is not persisted. Intended for use by the Zigbee stack. - */ - INTERNAL, -}; - -/** - * Derived keys are calculated when performing Zigbee crypto operations. The stack makes use of these derivations. - * Compounding derivations can be specified by using an or-equals on two derived types if applicable; - * this is limited to performing the key-transport, key-load, or verify-key hashes on either the TC Swap Out or TC Hashed Link keys. - * uint16_t - */ -export enum SecManDerivedKeyType { - /** Perform no derivation; use the key as is. */ - NONE = 0x0000, - /** Perform the Key-Transport-Key hash. */ - KEY_TRANSPORT_KEY = 0x0001, - /** Perform the Key-Load-Key hash. */ - KEY_LOAD_KEY = 0x0002, - /** Perform the Verify Key hash. */ - VERIFY_KEY = 0x0004, - /** Perform a simple AES hash of the key for TC backup. */ - TC_SWAP_OUT_KEY = 0x0008, - /** For a TC using hashed link keys, hashed the root key against the supplied EUI in context. */ - TC_HASHED_LINK_KEY = 0x0010, -}; - -/** - * Security Manager context flags. - * uint8_t - */ -export enum SecManFlag { - NONE = 0x00, - /** - * For export APIs, this flag indicates the key_index parameter is valid in - * the ::sl_zb_sec_man_context_t structure. This bit is set by the caller - * when intending to search for a key by key_index. This flag has no - * significance for import APIs. */ - KEY_INDEX_IS_VALID = 0x01, - /** - * For export APIs, this flag indicates the eui64 parameter is valid in the - * ::sl_zb_sec_man_context_t structure. This bit is set by the caller when - * intending to search for a key by eui64. It is also set when searching by - * key_index and an entry is found. This flag has no significance for import - * APIs. */ - EUI_IS_VALID = 0x02, - /** - * Internal use only. This indicates that the transient key being added is an - * unconfirmed, updated key. This bit is set when we add a transient key and - * the ::EmberTcLinkKeyRequestPolicy policy - * is ::EMBER_ALLOW_TC_LINK_KEY_REQUEST_AND_GENERATE_NEW_KEY, whose behavior - * dictates that we generate a new, unconfirmed key, send it to the requester, - * and await for a Verify Key Confirm message. */ - UNCONFIRMED_TRANSIENT_KEY = 0x04, - /** - * Internal use only. This indicates that the key being added was derived via - * dynamic link key negotiation. This may be used in conjunction with the above - * ::UNCONFIRMED_TRANSIENT_KEY while the derived link key awaits - * confirmation - */ - AUTHENTICATED_DYNAMIC_LINK_KEY = 0x08, - /** - * Internal use only. This indicates that the "key" being added is instead the - * symmetric passphrase to be stored in the link key table. This flag will trigger the - * addition of the KEY_TABLE_SYMMETRIC_PASSPHRASE bitmask when storing the symmetric - * passphrase so that it can be differentiated from other keys with the same EUI64. - */ - SYMMETRIC_PASSPHRASE = 0x10, -}; - -/** This denotes the status of an attempt to establish a key with another device. */ -export enum EmberKeyStatus { - STATUS_NONE = 0x00, - APP_LINK_KEY_ESTABLISHED = 0x01, - TRUST_CENTER_LINK_KEY_ESTABLISHED = 0x03, - - ESTABLISHMENT_TIMEOUT = 0x04, - TABLE_FULL = 0x05, - - // These are success status values applying only to the Trust Center answering key requests. - TC_RESPONDED_TO_KEY_REQUEST = 0x06, - TC_APP_KEY_SENT_TO_REQUESTER = 0x07, - - // These are failure status values applying only to the - // Trust Center answering key requests. - TC_RESPONSE_TO_KEY_REQUEST_FAILED = 0x08, - TC_REQUEST_KEY_TYPE_NOT_SUPPORTED = 0x09, - TC_NO_LINK_KEY_FOR_REQUESTER = 0x0A, - TC_REQUESTER_EUI64_UNKNOWN = 0x0B, - TC_RECEIVED_FIRST_APP_KEY_REQUEST = 0x0C, - TC_TIMEOUT_WAITING_FOR_SECOND_APP_KEY_REQUEST = 0x0D, - TC_NON_MATCHING_APP_KEY_REQUEST_RECEIVED = 0x0E, - TC_FAILED_TO_SEND_APP_KEYS = 0x0F, - TC_FAILED_TO_STORE_APP_KEY_REQUEST = 0x10, - TC_REJECTED_APP_KEY_REQUEST = 0x11, - TC_FAILED_TO_GENERATE_NEW_KEY = 0x12, - TC_FAILED_TO_SEND_TC_KEY = 0x13, - - // These are generic status values for a key requester. - TRUST_CENTER_IS_PRE_R21 = 0x1E, - - // These are status values applying only to the Trust Center verifying link keys. - TC_REQUESTER_VERIFY_KEY_TIMEOUT = 0x32, - TC_REQUESTER_VERIFY_KEY_FAILURE = 0x33, - TC_REQUESTER_VERIFY_KEY_SUCCESS = 0x34, - - // These are status values applying only to the key requester - // verifying link keys. - VERIFY_LINK_KEY_FAILURE = 0x64, - VERIFY_LINK_KEY_SUCCESS = 0x65, -}; - -/** This bitmask describes the presence of fields within the ::EmberKeyStruct. uint16_t */ -export enum EmberKeyStructBitmask { - /** This indicates that the key has a sequence number associated with it. (i.e., a Network Key). */ - HAS_SEQUENCE_NUMBER = 0x0001, - /** This indicates that the key has an outgoing frame counter and the corresponding value within the ::EmberKeyStruct has been populated.*/ - HAS_OUTGOING_FRAME_COUNTER = 0x0002, - /** This indicates that the key has an incoming frame counter and the corresponding value within the ::EmberKeyStruct has been populated.*/ - HAS_INCOMING_FRAME_COUNTER = 0x0004, - /** - * This indicates that the key has an associated Partner EUI64 address and the corresponding value - * within the ::EmberKeyStruct has been populated. - */ - HAS_PARTNER_EUI64 = 0x0008, - /** - * This indicates the key is authorized for use in APS data messages. - * If the key is not authorized for use in APS data messages it has not yet gone through a key agreement protocol, such as CBKE (i.e., ECC). - */ - IS_AUTHORIZED = 0x0010, - /** - * This indicates that the partner associated with the link is a sleepy end device. - * This bit is set automatically if the local device hears a device announce from the partner indicating it is not an 'RX on when idle' device. - */ - PARTNER_IS_SLEEPY = 0x0020, - /** - * This indicates that the transient key which is being added is unconfirmed. - * This bit is set when we add a transient key while the EmberTcLinkKeyRequestPolicy is EMBER_ALLOW_TC_LINK_KEY_REQUEST_AND_GENERATE_NEW_KEY - */ - UNCONFIRMED_TRANSIENT = 0x0040, - /** This indicates that the actual key data is stored in PSA, and the respective PSA ID is recorded in the psa_id field. */ - HAS_PSA_ID = 0x0080, - /** - * This indicates that the keyData field has valid data. On certain parts and depending on the security configuration, - * keys may live in secure storage and are not exportable. In such cases, keyData will not house the actual key contents. - */ - HAS_KEY_DATA = 0x0100, - /** - * This indicates that the key represents a Device Authentication Token and is not an encryption key. - * The Authentication token is persisted for the lifetime of the device on the network and used to validate and update the device connection. - * It is only removed when the device leaves or is decommissioned from the network - */ - IS_AUTHENTICATION_TOKEN = 0x0200, - /** This indicates that the key has been derived by the Dynamic Link Key feature. */ - DLK_DERIVED = 0x0400, - /** This indicates that the device this key is being used to communicate with supports the APS frame counter synchronization procedure. */ - FC_SYNC_SUPPORTED = 0x0800, -}; - -/** - * The Status of the Update Device message sent to the Trust Center. - * The device may have joined or rejoined insecurely, rejoined securely, or left. - * MAC Security has been deprecated and therefore there is no secure join. - * These map to the actual values within the APS Command frame so they cannot be arbitrarily changed. - * uint8_t - */ -export enum EmberDeviceUpdate { - STANDARD_SECURITY_SECURED_REJOIN = 0, - STANDARD_SECURITY_UNSECURED_JOIN = 1, - DEVICE_LEFT = 2, - STANDARD_SECURITY_UNSECURED_REJOIN = 3, -}; - -/** The decision made by the Trust Center when a node attempts to join. uint8_t */ -export enum EmberJoinDecision { - /** Allow the node to join. The node has the key. */ - USE_PRECONFIGURED_KEY = 0, - /** Allow the node to join. Send the key to the node. */ - SEND_KEY_IN_THE_CLEAR, - /** Deny join. */ - DENY_JOIN, - /** Take no action. */ - NO_ACTION, - /** Allow rejoins only.*/ - ALLOW_REJOINS_ONLY -}; - -/** A bitmask indicating the state of the ZLL device. This maps directly to the ZLL information field in the scan response. uint16_t */ -export enum EmberZllState { - /** No state. */ - NONE = 0x0000, - /** The device is factory new. */ - FACTORY_NEW = 0x0001, - /** The device is capable of assigning addresses to other devices. */ - ADDRESS_ASSIGNMENT_CAPABLE = 0x0002, - /** The device is initiating a link operation. */ - LINK_INITIATOR = 0x0010, - /** The device is requesting link priority. */ - LINK_PRIORITY_REQUEST = 0x0020, - /** The device is a ZigBee 3.0 device. */ - PROFILE_INTEROP = 0x0080, - /** The device is on a non-ZLL network. */ - NON_ZLL_NETWORK = 0x0100, - /** Internal use: the ZLL token's key values point to a PSA key identifier */ - TOKEN_POINTS_TO_PSA_ID = 0x0200, -}; - -/** Differentiates among ZLL network operations. */ -export enum EzspZllNetworkOperation { - /** ZLL form network command. */ - FORM_NETWORK = 0x00, - /** ZLL join target command. */ - JOIN_TARGET = 0x01 -}; - -/** The key encryption algorithms supported by the stack. */ -export enum EmberZllKeyIndex { - /** The key encryption algorithm for use during development. */ - DEVELOPMENT = 0x00, - /** The key encryption algorithm shared by all certified devices. */ - MASTER = 0x04, - /** The key encryption algorithm for use during development and certification. */ - CERTIFICATION = 0x0F, -}; - -/** uint8_t */ -export enum EmberGpApplicationId { - /** Source identifier. */ - SOURCE_ID = 0x00, - /** IEEE address. */ - IEEE_ADDRESS = 0x02, -}; - -/** Green Power Security Level. uint8_t */ -export enum EmberGpSecurityLevel { - /** No Security */ - NONE = 0x00, - /** Reserved */ - RESERVED = 0x01, - /** 4 Byte Frame Counter and 4 Byte MIC */ - FC_MIC = 0x02, - /** 4 Byte Frame Counter and 4 Byte MIC with encryption */ - FC_MIC_ENCRYPTED = 0x03, -}; - -/** Green Power Security Security Key Type. uint8_t */ -export enum EmberGpKeyType { - /** No Key */ - NONE = 0x00, - /** GP Security Key Type is Zigbee Network Key */ - NWK = 0x01, - /** GP Security Key Type is Group Key */ - GPD_GROUP = 0x02, - /** GP Security Key Type is Derived Network Key */ - NWK_DERIVED = 0x03, - /** GP Security Key Type is Out Of Box Key */ - GPD_OOB = 0x04, - /** GP Security Key Type is GPD Derived Key */ - GPD_DERIVED = 0x07, -}; - -/** uint8_t */ -export enum EmberGpProxyTableEntryStatus { - /** The GP table entry is in use for a Proxy Table Entry. */ - ACTIVE = 0x01, - /** The proxy table entry is not in use. */ - UNUSED = 0xFF, -}; - -/** GP Sink Type. */ -export enum EmberGpSinkType { - /** Sink Type is Full Unicast */ - FULL_UNICAST, - /** Sink Type is Derived groupcast, the group ID is derived from the GpdId during commissioning. - * The sink is added to the APS group with that groupId. - */ - D_GROUPCAST, - /** Sink type GROUPCAST, the groupId can be obtained from the APS group table - * or from the sink table. - */ - GROUPCAST, - /** Sink Type is Light Weight Unicast. */ - LW_UNICAST, - /** Unused sink type */ - UNUSED = 0xFF -}; - -/** uint8_t */ -export enum EmberGpSinkTableEntryStatus { - /** The GP table entry is in use for a Sink Table Entry. */ - ACTIVE = 0x01, - /** The proxy table entry is not in use. */ - UNUSED = 0xFF, -}; diff --git a/src/adapter/ember/ezsp/buffalo.ts b/src/adapter/ember/ezsp/buffalo.ts deleted file mode 100644 index 46634c6a1eb..00000000000 --- a/src/adapter/ember/ezsp/buffalo.ts +++ /dev/null @@ -1,1269 +0,0 @@ -/* istanbul ignore file */ -import Buffalo from "../../../buffalo/buffalo"; -import {GP_SINK_LIST_ENTRIES} from "../consts"; -import {EmberGpApplicationId, EmberGpSinkType, EzspStatus} from "../enums"; -import { - EmberAesMmoHashContext, - EmberApsFrame, - EmberBeaconClassificationParams, - EmberBeaconData, - EmberBeaconIterator, - EmberBindingTableEntry, - EmberCertificate283k1Data, - EmberCertificateData, - EmberChildData, - EmberCurrentSecurityState, - EmberDutyCycleLimits, - EmberGpAddress, - EmberGpProxyTableEntry, - EmberGpSinkListEntry, - EmberGpSinkTableEntry, - EmberInitialSecurityState, - EmberKeyData, - EmberMessageDigest, - EmberMultiPhyRadioParameters, - EmberMulticastTableEntry, - EmberNeighborTableEntry, - EmberNetworkInitStruct, - EmberNetworkParameters, - EmberPerDeviceDutyCycle, - EmberPrivateKey283k1Data, - EmberPrivateKeyData, - EmberPublicKey283k1Data, - EmberPublicKeyData, - EmberRouteTableEntry, - EmberSignature283k1Data, - EmberSignatureData, - EmberSmacData, - EmberTokTypeStackZllData, - EmberTokTypeStackZllSecurity, - EmberTokenData, - EmberTokenInfo, - EmberTransientKeyData, - EmberZigbeeNetwork, - EmberZllAddressAssignment, - EmberZllDeviceInfoRecord, - EmberZllInitialSecurityState, - EmberZllNetwork, - EmberZllSecurityAlgorithmData, - SecManAPSKeyMetadata, - SecManContext, - SecManKey, - SecManNetworkKeyInfo -} from "../types"; -import {highByte} from "../utils/math"; -import { - EMBER_ENCRYPTION_KEY_SIZE, - EMBER_CERTIFICATE_SIZE, - EMBER_PUBLIC_KEY_SIZE, - EMBER_PRIVATE_KEY_SIZE, - EMBER_SMAC_SIZE, - EMBER_SIGNATURE_SIZE, - EMBER_AES_HASH_BLOCK_SIZE, - EMBER_CERTIFICATE_283K1_SIZE, - EMBER_PUBLIC_KEY_283K1_SIZE, - EMBER_PRIVATE_KEY_283K1_SIZE, - EMBER_SIGNATURE_283K1_SIZE, - EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX, - EZSP_EXTENDED_FRAME_FORMAT_VERSION_MASK, - EZSP_EXTENDED_FRAME_FORMAT_VERSION, - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - EZSP_EXTENDED_PARAMETERS_INDEX, - EZSP_EXTENDED_FRAME_CONTROL_RESERVED_MASK, - EZSP_FRAME_CONTROL_INDEX, - EZSP_FRAME_ID_INDEX, - EZSP_PARAMETERS_INDEX, - EZSP_EXTENDED_FRAME_ID_HB_INDEX, - EZSP_EXTENDED_FRAME_ID_LB_INDEX, - EXTENDED_PAN_ID_SIZE, -} from "./consts"; -import {EzspFrameID} from "./enums"; - - -export class EzspBuffalo extends Buffalo { - - public getBufferLength(): number { - return this.buffer.length; - } - - /** Set the position of the internal position tracker. */ - public setPosition(position: number): void { - this.position = position; - } - - /** - * Set the byte at given position without affecting the internal position tracker. - * @param position - * @param value - */ - public setCommandByte(position: number, value: number): void { - this.buffer.writeUInt8(value, position); - } - - /** - * Get the byte at given position without affecting the internal position tracker. - * @param position - * @returns - */ - public getCommandByte(position: number): number { - return this.buffer.readUInt8(position); - } - - /** - * Get the byte at given position without affecting the internal position tracker. - * @param position - * @returns - */ - public getResponseByte(position: number): number { - return this.buffer.readUInt8(position); - } - - public getExtFrameControl(): number { - return (this.getResponseByte(EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX) << 8 | this.getResponseByte(EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX)); - } - - public getExtFrameId(): EzspFrameID { - return (this.getResponseByte(EZSP_EXTENDED_FRAME_ID_HB_INDEX) << 8 | this.getResponseByte(EZSP_EXTENDED_FRAME_ID_LB_INDEX)); - } - - public getFrameId(): EzspFrameID { - if ((this.getResponseByte(EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX) & EZSP_EXTENDED_FRAME_FORMAT_VERSION_MASK) - === EZSP_EXTENDED_FRAME_FORMAT_VERSION) { - return this.getExtFrameId(); - } else { - return this.getResponseByte(EZSP_FRAME_ID_INDEX) as EzspFrameID; - } - } - - /** - * Get the frame control, ID and params index according to format version. - * Throws if frame control is unsupported (using reserved). - * @returns Anything but SUCCESS should stop further processing. - */ - public getResponseMetadata(): [status: EzspStatus, frameControl: number, frameId: EzspFrameID, parametersIndex: number] { - let status: EzspStatus = EzspStatus.SUCCESS; - let frameControl: number; - let frameId: EzspFrameID; - let parametersIndex: number; - - if ((this.getResponseByte(EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX) & EZSP_EXTENDED_FRAME_FORMAT_VERSION_MASK) - === EZSP_EXTENDED_FRAME_FORMAT_VERSION) { - // use extended ezsp frame format - frameControl = this.getExtFrameControl(); - frameId = this.getExtFrameId(); - parametersIndex = EZSP_EXTENDED_PARAMETERS_INDEX; - - if (highByte(frameControl) & EZSP_EXTENDED_FRAME_CONTROL_RESERVED_MASK) { - // reject if unsupported frame - status = EzspStatus.ERROR_UNSUPPORTED_CONTROL; - } - } else { - // use legacy ezsp frame format - frameControl = this.getResponseByte(EZSP_FRAME_CONTROL_INDEX); - frameId = this.getResponseByte(EZSP_FRAME_ID_INDEX) as EzspFrameID; - parametersIndex = EZSP_PARAMETERS_INDEX; - } - - return [status, frameControl, frameId, parametersIndex]; - } - - /** - * Get a copy of the rest of the buffer (from current position to end). - * WARNING: Make sure the length is appropriate, if alloc'ed longer, it will return everything until the end. - * @returns - */ - public readRest(): Buffer { - return Buffer.from(this.buffer.subarray(this.position)); - } - - /** - * This is mostly used for payload/encryption stuff. - * Copies the buffer to avoid memory referencing issues since Ezsp has a single buffer allocated. - * @param length - * @returns - */ - protected readBufferCopy(length: number): Buffer { - return Buffer.from(this.readBuffer(length)); - } - - /** - * Write a uint8_t for payload length, followed by payload buffer (copied at post-length position). - * - * WARNING: `payload` must have a valid length (as in, not a Buffer allocated to longer length). - * Should be passed with getWritten() in most cases. - * @param payload - */ - public writePayload(payload: Buffer): void { - this.writeUInt8(payload.length); - - this.position += payload.copy(this.buffer, this.position); - } - - /** - * Read a uint8_t for payload length, followed by payload buffer (using post-length position). - * @returns - */ - public readPayload(): Buffer { - const messageLength = this.readUInt8(); - - return this.readBufferCopy(messageLength); - } - - public writeEmberNetworkParameters(value: EmberNetworkParameters): void { - this.writeListUInt8(value.extendedPanId); - this.writeUInt16(value.panId); - this.writeUInt8(value.radioTxPower); - this.writeUInt8(value.radioChannel); - this.writeUInt8(value.joinMethod); - this.writeUInt16(value.nwkManagerId); - this.writeUInt8(value.nwkUpdateId); - this.writeUInt32(value.channels); - } - - public readEmberNetworkParameters(): EmberNetworkParameters { - const extendedPanId = this.readListUInt8({length: EXTENDED_PAN_ID_SIZE}); - const panId = this.readUInt16(); - const radioTxPower = this.readUInt8(); - const radioChannel = this.readUInt8(); - const joinMethod = this.readUInt8(); - const nwkManagerId = this.readUInt16(); - const nwkUpdateId = this.readUInt8(); - const channels = this.readUInt32(); - - return { - extendedPanId, - panId, - radioTxPower, - radioChannel, - joinMethod, - nwkManagerId, - nwkUpdateId, - channels, - }; - } - - public writeEmberMultiPhyRadioParameters(value: EmberMultiPhyRadioParameters): void { - this.writeUInt8(value.radioTxPower); - this.writeUInt8(value.radioPage); - this.writeUInt8(value.radioChannel); - } - - public readEmberMultiPhyRadioParameters(): EmberMultiPhyRadioParameters { - const radioTxPower = this.readUInt8(); - const radioPage = this.readUInt8(); - const radioChannel = this.readUInt8(); - - return {radioTxPower, radioPage, radioChannel}; - } - - public writeEmberApsFrame(value: EmberApsFrame): void { - this.writeUInt16(value.profileId); - this.writeUInt16(value.clusterId); - this.writeUInt8(value.sourceEndpoint); - this.writeUInt8(value.destinationEndpoint); - this.writeUInt16(value.options); - this.writeUInt16(value.groupId); - this.writeUInt8(value.sequence); - // this.writeUInt8(value.radius);// XXX: not in gecko_sdk, appended with separate param - } - - public readEmberApsFrame(): EmberApsFrame { - const profileId = this.readUInt16(); - const clusterId = this.readUInt16(); - const sourceEndpoint = this.readUInt8(); - const destinationEndpoint = this.readUInt8(); - const options = this.readUInt16(); - const groupId = this.readUInt16(); - const sequence = this.readUInt8(); - // const radius = this.readUInt8();// XXX: not in gecko_sdk, appended with separate param - - return { - profileId, - clusterId, - sourceEndpoint, - destinationEndpoint, - options, - groupId, - sequence, - // radius, - }; - } - - public writeEmberBindingTableEntry(value: EmberBindingTableEntry): void { - this.writeUInt8(value.type); - this.writeUInt8(value.local); - this.writeUInt16(value.clusterId); - this.writeUInt8(value.remote); - this.writeIeeeAddr(value.identifier); - this.writeUInt8(value.networkIndex); - } - - public readEmberBindingTableEntry(): EmberBindingTableEntry { - const type = this.readUInt8(); - const local = this.readUInt8(); - const clusterId = this.readUInt16(); - const remote = this.readUInt8(); - const identifier = this.readIeeeAddr(); - const networkIndex = this.readUInt8(); - - return { - type, - local, - clusterId, - remote, - identifier, - networkIndex, - }; - } - - public writeEmberMulticastTableEntry(value: EmberMulticastTableEntry): void { - this.writeUInt16(value.multicastId); - this.writeUInt8(value.endpoint); - this.writeUInt8(value.networkIndex); - } - - public readEmberMulticastTableEntry(): EmberMulticastTableEntry { - const multicastId = this.readUInt16(); - const endpoint = this.readUInt8(); - // XXX: not in gecko_sdk? as workaround check length for now since used at end in just one place - const networkIndex = this.isMore() ? this.readUInt8() : 0x00; - - return {multicastId, endpoint, networkIndex}; - } - - public writeEmberBeaconClassificationParams(value: EmberBeaconClassificationParams): void { - this.writeUInt8(value.minRssiForReceivingPkts); - this.writeUInt16(value.beaconClassificationMask); - } - - public readEmberBeaconClassificationParams(): EmberBeaconClassificationParams { - const minRssiForReceivingPkts = this.readUInt8();// Int8... - const beaconClassificationMask = this.readUInt16(); - - return {minRssiForReceivingPkts, beaconClassificationMask}; - } - - public writeEmberNeighborTableEntry(value: EmberNeighborTableEntry): void { - this.writeUInt16(value.shortId); - this.writeUInt8(value.averageLqi); - this.writeUInt8(value.inCost); - this.writeUInt8(value.outCost); - this.writeUInt8(value.age); - this.writeIeeeAddr(value.longId); - } - - public readEmberNeighborTableEntry(): EmberNeighborTableEntry { - const shortId = this.readUInt16(); - const averageLqi = this.readUInt8(); - const inCost = this.readUInt8(); - const outCost = this.readUInt8(); - const age = this.readUInt8(); - const longId = this.readIeeeAddr(); - - return { - shortId, - averageLqi, - inCost, - outCost, - age, - longId, - }; - } - - public writeEmberRouteTableEntry(value: EmberRouteTableEntry): void { - this.writeUInt16(value.destination); - this.writeUInt16(value.nextHop); - this.writeUInt8(value.status); - this.writeUInt8(value.age); - this.writeUInt8(value.concentratorType); - this.writeUInt8(value.routeRecordState); - } - - public readEmberRouteTableEntry(): EmberRouteTableEntry { - const destination = this.readUInt16(); - const nextHop = this.readUInt16(); - const status = this.readUInt8(); - const age = this.readUInt8(); - const concentratorType = this.readUInt8(); - const routeRecordState = this.readUInt8(); - - return { - destination, - nextHop, - status, - age, - concentratorType, - routeRecordState, - }; - } - - public writeEmberKeyData(value: EmberKeyData): void { - this.writeBuffer(value.contents, EMBER_ENCRYPTION_KEY_SIZE); - } - - public readEmberKeyData(): EmberKeyData { - const contents = this.readBufferCopy(EMBER_ENCRYPTION_KEY_SIZE); - - return {contents}; - } - - public writeSecManKey(value: SecManKey): void { - this.writeEmberKeyData(value); - } - - public readSecManKey(): SecManKey { - return this.readEmberKeyData(); - } - - public writeSecManContext(value: SecManContext): void { - this.writeUInt8(value.coreKeyType); - this.writeUInt8(value.keyIndex); - this.writeUInt16(value.derivedType); - this.writeIeeeAddr(value.eui64); - this.writeUInt8(value.multiNetworkIndex); - this.writeUInt8(value.flags); - this.writeUInt32(value.psaKeyAlgPermission); - } - - public readSecManContext(): SecManContext { - const core_key_type = this.readUInt8(); - const key_index = this.readUInt8(); - const derived_type = this.readUInt16(); - const eui64 = this.readIeeeAddr(); - const multi_network_index = this.readUInt8(); - const flags = this.readUInt8(); - const psa_key_alg_permission = this.readUInt32(); - - return { - coreKeyType: core_key_type, - keyIndex: key_index, - derivedType: derived_type, - eui64, - multiNetworkIndex: multi_network_index, - flags, - psaKeyAlgPermission: psa_key_alg_permission, - }; - } - - public writeSecManNetworkKeyInfo(value: SecManNetworkKeyInfo): void { - this.writeUInt8(value.networkKeySet ? 1 : 0); - this.writeUInt8(value.alternateNetworkKeySet ? 1 : 0); - this.writeUInt8(value.networkKeySequenceNumber); - this.writeUInt8(value.altNetworkKeySequenceNumber); - this.writeUInt32(value.networkKeyFrameCounter); - } - - public readSecManNetworkKeyInfo(): SecManNetworkKeyInfo { - const networkKeySet = this.readUInt8() === 1 ? true : false; - const alternateNetworkKeySet = this.readUInt8() === 1 ? true : false; - const networkKeySequenceNumber = this.readUInt8(); - const altNetworkKeySequenceNumber = this.readUInt8(); - const networkKeyFrameCounter = this.readUInt32(); - - return { - networkKeySet: networkKeySet, - alternateNetworkKeySet: alternateNetworkKeySet, - networkKeySequenceNumber: networkKeySequenceNumber, - altNetworkKeySequenceNumber: altNetworkKeySequenceNumber, - networkKeyFrameCounter: networkKeyFrameCounter, - }; - } - - public writeSecManAPSKeyMetadata(value: SecManAPSKeyMetadata): void { - this.writeUInt16(value.bitmask); - this.writeUInt32(value.outgoingFrameCounter); - this.writeUInt32(value.incomingFrameCounter); - this.writeUInt16(value.ttlInSeconds); - } - - 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(); - - return { - bitmask, - outgoingFrameCounter: outgoing_frame_counter, - incomingFrameCounter: incoming_frame_counter, - ttlInSeconds: ttl_in_seconds, - }; - } - - public writeEmberTransientKeyData(value: EmberTransientKeyData): void { - this.writeIeeeAddr(value.eui64); - this.writeEmberKeyData(value.keyData); - this.writeUInt32(value.incomingFrameCounter); - this.writeUInt16(value.bitmask); - this.writeUInt16(value.remainingTimeSeconds); - this.writeUInt8(value.networkIndex); - } - - public readEmberTransientKeyData(): EmberTransientKeyData { - const eui64 = this.readIeeeAddr(); - const keyData = this.readEmberKeyData(); - const incomingFrameCounter = this.readUInt32(); - const bitmask = this.readUInt16(); - const remainingTimeSeconds = this.readUInt16(); - const networkIndex = this.readUInt8(); - - return { - eui64, - keyData, - incomingFrameCounter, - bitmask, - remainingTimeSeconds, - networkIndex, - }; - } - - public writeEmberInitialSecurityState(value: EmberInitialSecurityState): void { - this.writeUInt16(value.bitmask); - this.writeEmberKeyData(value.preconfiguredKey); - this.writeEmberKeyData(value.networkKey); - this.writeUInt8(value.networkKeySequenceNumber); - this.writeIeeeAddr(value.preconfiguredTrustCenterEui64); - } - - public readEmberInitialSecurityState(): EmberInitialSecurityState { - const bitmask = this.readUInt16(); - const preconfiguredKey = this.readEmberKeyData(); - const networkKey = this.readEmberKeyData(); - const networkKeySequenceNumber = this.readUInt8(); - const preconfiguredTrustCenterEui64 = this.readIeeeAddr(); - - return { - bitmask, - preconfiguredKey, - networkKey, - networkKeySequenceNumber, - preconfiguredTrustCenterEui64, - }; - } - - public writeEmberCurrentSecurityState(value: EmberCurrentSecurityState): void { - this.writeUInt16(value.bitmask); - this.writeIeeeAddr(value.trustCenterLongAddress); - } - - public readEmberCurrentSecurityState(): EmberCurrentSecurityState { - const bitmask = this.readUInt16(); - const trustCenterLongAddress = this.readIeeeAddr(); - - return {bitmask, trustCenterLongAddress}; - } - - public writeEmberChildData(value: EmberChildData): void { - this.writeIeeeAddr(value.eui64); - this.writeUInt8(value.type); - this.writeUInt16(value.id); - this.writeUInt8(value.phy); - this.writeUInt8(value.power); - this.writeUInt8(value.timeout); - this.writeUInt32(value.remainingTimeout); - } - - public readEmberChildData(): EmberChildData { - const eui64 = this.readIeeeAddr(); - const type = this.readUInt8(); - const id = this.readUInt16(); - const phy = this.readUInt8(); - const power = this.readUInt8(); - const timeout = this.readUInt8(); - const remainingTimeout = this.readUInt32(); - - return { - eui64, - type, - id, - phy, - power, - timeout, - remainingTimeout, - }; - } - - public readEmberZigbeeNetwork(): EmberZigbeeNetwork { - const channel = this.readUInt8(); - const panId = this.readUInt16(); - const extendedPanId = this.readListUInt8({length: EXTENDED_PAN_ID_SIZE}); - const allowingJoin = this.readUInt8(); - const stackProfile = this.readUInt8(); - const nwkUpdateId = this.readUInt8(); - - return { - channel, - panId, - extendedPanId, - allowingJoin, - stackProfile, - nwkUpdateId, - }; - } - - public writeEmberZigbeeNetwork(value: EmberZigbeeNetwork): void { - this.writeUInt8(value.channel); - this.writeUInt16(value.panId); - this.writeListUInt8(value.extendedPanId); - this.writeUInt8(value.allowingJoin); - this.writeUInt8(value.stackProfile); - this.writeUInt8(value.nwkUpdateId); - } - - public writeEmberCertificateData(value: EmberCertificateData): void { - this.writeBuffer(value.contents, EMBER_CERTIFICATE_SIZE); - } - - public readEmberCertificateData(): EmberCertificateData { - const contents = this.readBufferCopy(EMBER_CERTIFICATE_SIZE); - - return {contents}; - } - - public writeEmberPublicKeyData(value: EmberPublicKeyData): void { - this.writeBuffer(value.contents, EMBER_PUBLIC_KEY_SIZE); - } - - public readEmberPublicKeyData(): EmberPublicKeyData { - const contents = this.readBufferCopy(EMBER_PUBLIC_KEY_SIZE); - - return {contents}; - } - - public writeEmberPrivateKeyData(value: EmberPrivateKeyData): void { - this.writeBuffer(value.contents, EMBER_PRIVATE_KEY_SIZE); - } - - public readEmberPrivateKeyData(): EmberPrivateKeyData { - const contents = this.readBufferCopy(EMBER_PRIVATE_KEY_SIZE); - - return {contents}; - } - - public writeEmberSmacData(value: EmberSmacData): void { - this.writeBuffer(value.contents, EMBER_SMAC_SIZE); - } - - public readEmberSmacData(): EmberSmacData { - const contents = this.readBufferCopy(EMBER_SMAC_SIZE); - - return {contents}; - } - - public writeEmberSignatureData(value: EmberSignatureData): void { - this.writeBuffer(value.contents, EMBER_SIGNATURE_SIZE); - } - - public readEmberSignatureData(): EmberSignatureData { - const contents = this.readBufferCopy(EMBER_SIGNATURE_SIZE); - - return {contents}; - } - - public writeEmberCertificate283k1Data(value: EmberCertificate283k1Data): void { - this.writeBuffer(value.contents, EMBER_CERTIFICATE_283K1_SIZE); - } - - public readEmberCertificate283k1Data(): EmberCertificate283k1Data { - const contents = this.readBufferCopy(EMBER_CERTIFICATE_283K1_SIZE); - - return {contents}; - } - - public writeEmberPublicKey283k1Data(value: EmberPublicKey283k1Data): void { - this.writeBuffer(value.contents, EMBER_PUBLIC_KEY_283K1_SIZE); - } - - public readEmberPublicKey283k1Data(): EmberPublicKey283k1Data { - const contents = this.readBufferCopy(EMBER_PUBLIC_KEY_283K1_SIZE); - - return {contents}; - } - - public writeEmberPrivateKey283k1Data(value: EmberPrivateKey283k1Data): void { - this.writeBuffer(value.contents, EMBER_PRIVATE_KEY_283K1_SIZE); - } - - public readEmberPrivateKey283k1Data(): EmberPrivateKey283k1Data { - const contents = this.readBufferCopy(EMBER_PRIVATE_KEY_283K1_SIZE); - - return {contents}; - } - - public writeEmberSignature283k1Data(value: EmberSignature283k1Data): void { - this.writeBuffer(value.contents, EMBER_SIGNATURE_283K1_SIZE); - } - - public readEmberSignature283k1Data(): EmberSignature283k1Data { - const contents = this.readBufferCopy(EMBER_SIGNATURE_283K1_SIZE); - - return {contents}; - } - - public writeEmberAesMmoHashContext(context: EmberAesMmoHashContext): void { - this.writeBuffer(context.result, EMBER_AES_HASH_BLOCK_SIZE); - this.writeUInt32(context.length); - } - - public readEmberAesMmoHashContext(): EmberAesMmoHashContext { - const result = this.readBufferCopy(EMBER_AES_HASH_BLOCK_SIZE); - const length = this.readUInt32(); - - return {result, length}; - } - - public writeEmberMessageDigest(value: EmberMessageDigest): void { - this.writeBuffer(value.contents, EMBER_AES_HASH_BLOCK_SIZE); - } - - public readEmberMessageDigest(): EmberMessageDigest { - const contents = this.readBufferCopy(EMBER_AES_HASH_BLOCK_SIZE); - - return {contents}; - } - - public writeEmberNetworkInitStruct(networkInitStruct: EmberNetworkInitStruct): void { - this.writeUInt16(networkInitStruct.bitmask); - } - - public readEmberNetworkInitStruct(): EmberNetworkInitStruct { - const bitmask = this.readUInt16(); - - return {bitmask}; - } - - public writeEmberZllNetwork(network: EmberZllNetwork): void { - this.writeEmberZigbeeNetwork(network.zigbeeNetwork); - this.writeEmberZllSecurityAlgorithmData(network.securityAlgorithm); - this.writeIeeeAddr(network.eui64); - this.writeUInt16(network.nodeId); - this.writeUInt16(network.state); - this.writeUInt8(network.nodeType); - this.writeUInt8(network.numberSubDevices); - this.writeUInt8(network.totalGroupIdentifiers); - this.writeUInt8(network.rssiCorrection); - } - - public readEmberZllNetwork(): EmberZllNetwork { - const zigbeeNetwork = this.readEmberZigbeeNetwork(); - const securityAlgorithm = this.readEmberZllSecurityAlgorithmData(); - const eui64 = this.readIeeeAddr(); - const nodeId = this.readUInt16(); - const state = this.readUInt16(); - const nodeType = this.readUInt8(); - const numberSubDevices = this.readUInt8(); - const totalGroupIdentifiers = this.readUInt8(); - const rssiCorrection = this.readUInt8(); - - return { - zigbeeNetwork, - securityAlgorithm, - eui64, - nodeId, - state, - nodeType, - numberSubDevices, - totalGroupIdentifiers, - rssiCorrection, - }; - } - - public writeEmberZllSecurityAlgorithmData(data: EmberZllSecurityAlgorithmData): void { - this.writeUInt32(data.transactionId); - this.writeUInt32(data.responseId); - this.writeUInt16(data.bitmask); - } - - public readEmberZllSecurityAlgorithmData(): EmberZllSecurityAlgorithmData { - const transactionId = this.readUInt32(); - const responseId = this.readUInt32(); - const bitmask = this.readUInt16(); - - return {transactionId, responseId, bitmask}; - } - - public writeEmberZllInitialSecurityState(state: EmberZllInitialSecurityState): void { - this.writeUInt32(state.bitmask); - this.writeUInt8(state.keyIndex); - this.writeEmberKeyData(state.encryptionKey); - this.writeEmberKeyData(state.preconfiguredKey); - } - - public writeEmberTokTypeStackZllData(data: EmberTokTypeStackZllData): void { - this.writeUInt32(data.bitmask); - this.writeUInt16(data.freeNodeIdMin); - this.writeUInt16(data.freeNodeIdMax); - this.writeUInt16(data.myGroupIdMin); - this.writeUInt16(data.freeGroupIdMin); - this.writeUInt16(data.freeGroupIdMax); - this.writeUInt8(data.rssiCorrection); - } - - public readEmberTokTypeStackZllData(): EmberTokTypeStackZllData { - const bitmask = this.readUInt32(); - const freeNodeIdMin = this.readUInt16(); - const freeNodeIdMax = this.readUInt16(); - const myGroupIdMin = this.readUInt16(); - const freeGroupIdMin = this.readUInt16(); - const freeGroupIdMax = this.readUInt16(); - const rssiCorrection = this.readUInt8(); - - return { - bitmask, - freeNodeIdMin, - freeNodeIdMax, - myGroupIdMin, - freeGroupIdMin, - freeGroupIdMax, - rssiCorrection, - }; - } - - public writeEmberTokTypeStackZllSecurity(security: EmberTokTypeStackZllSecurity): void { - this.writeUInt32(security.bitmask); - this.writeUInt8(security.keyIndex); - this.writeBuffer(security.encryptionKey, EMBER_ENCRYPTION_KEY_SIZE); - this.writeBuffer(security.preconfiguredKey, EMBER_ENCRYPTION_KEY_SIZE); - } - - public readEmberTokTypeStackZllSecurity(): EmberTokTypeStackZllSecurity { - const bitmask = this.readUInt32(); - const keyIndex = this.readUInt8(); - const encryptionKey = this.readBufferCopy(EMBER_ENCRYPTION_KEY_SIZE); - const preconfiguredKey = this.readBufferCopy(EMBER_ENCRYPTION_KEY_SIZE); - - return { - bitmask, - keyIndex, - encryptionKey, - preconfiguredKey, - }; - } - - public writeEmberGpAddress(value: EmberGpAddress): void { - this.writeUInt8(value.applicationId); - - if (value.applicationId === EmberGpApplicationId.SOURCE_ID) { - this.writeUInt32(value.sourceId); - this.writeUInt32(value.sourceId);// filler - } else if (value.applicationId === EmberGpApplicationId.IEEE_ADDRESS) { - this.writeIeeeAddr(value.gpdIeeeAddress); - } - - this.writeUInt8(value.endpoint); - } - - public readEmberGpAddress(): EmberGpAddress { - const applicationId = this.readUInt8(); - - if (applicationId === EmberGpApplicationId.SOURCE_ID) { - const sourceId = this.readUInt32(); - this.readUInt32();// filler - const endpoint = this.readUInt8(); - - return {applicationId, sourceId, endpoint}; - } else if (applicationId === EmberGpApplicationId.IEEE_ADDRESS) { - const gpdIeeeAddress = this.readIeeeAddr(); - const endpoint = this.readUInt8(); - - return {applicationId, gpdIeeeAddress, endpoint}; - } - - return null; - } - - public readEmberGpSinkList(): EmberGpSinkListEntry[] { - const list: EmberGpSinkListEntry[] = []; - - for (let i = 0; i < GP_SINK_LIST_ENTRIES; i++) { - const type: EmberGpSinkType = this.readUInt8(); - - switch (type) { - case EmberGpSinkType.FULL_UNICAST: - case EmberGpSinkType.LW_UNICAST: - case EmberGpSinkType.UNUSED: - default: - const sinkNodeId = this.readUInt16(); - const sinkEUI = this.readIeeeAddr(); - - list.push({ - type, - unicast: { - sinkNodeId, - sinkEUI, - } - }); - break; - case EmberGpSinkType.D_GROUPCAST: - case EmberGpSinkType.GROUPCAST: - const alias = this.readUInt16(); - const groupID = this.readUInt16(); - - // fillers - this.readUInt16(); - this.readUInt16(); - this.readUInt16(); - - list.push({ - type, - groupcast: { - alias, - groupID, - } - }); - break; - } - } - - return list; - } - - public writeEmberGpSinkList(value: EmberGpSinkListEntry[]): void { - for (let i = 0; i < GP_SINK_LIST_ENTRIES; i++) { - this.writeUInt8(value[i].type); - - switch (value[i].type) { - case EmberGpSinkType.FULL_UNICAST: - case EmberGpSinkType.LW_UNICAST: - case EmberGpSinkType.UNUSED: - default: - this.writeUInt16(value[i].unicast.sinkNodeId); - this.writeIeeeAddr(value[i].unicast.sinkEUI);// changed 8 to const var - - break; - - case EmberGpSinkType.D_GROUPCAST: - case EmberGpSinkType.GROUPCAST: - this.writeUInt16(value[i].groupcast.alias); - this.writeUInt16(value[i].groupcast.groupID); - //fillers - this.writeUInt16(value[i].groupcast.alias); - this.writeUInt16(value[i].groupcast.groupID); - this.writeUInt16(value[i].groupcast.alias); - break; - } - } - } - - public readEmberGpProxyTableEntry(): EmberGpProxyTableEntry { - const status = this.readUInt8(); - const options = this.readUInt32(); - const gpd = this.readEmberGpAddress(); - const assignedAlias = this.readUInt16(); - const securityOptions = this.readUInt8(); - const gpdSecurityFrameCounter = this.readUInt32(); - const gpdKey = this.readEmberKeyData(); - const sinkList = this.readEmberGpSinkList(); - const groupcastRadius = this.readUInt8(); - const searchCounter = this.readUInt8(); - - return { - status, - options, - gpd, - assignedAlias, - securityOptions, - gpdSecurityFrameCounter, - gpdKey, - sinkList, - groupcastRadius, - searchCounter, - }; - } - - public writeEmberGpProxyTableEntry(value: EmberGpProxyTableEntry): void { - this.writeUInt8(value.status); - this.writeUInt32(value.options); - this.writeEmberGpAddress(value.gpd); - this.writeUInt16(value.assignedAlias); - this.writeUInt8(value.securityOptions); - this.writeUInt32(value.gpdSecurityFrameCounter); - this.writeEmberKeyData(value.gpdKey); - this.writeEmberGpSinkList(value.sinkList); - this.writeUInt8(value.groupcastRadius); - this.writeUInt8(value.searchCounter); - } - - public readEmberGpSinkTableEntry(): EmberGpSinkTableEntry { - const status = this.readUInt8(); - const options = this.readUInt16(); - const gpd = this.readEmberGpAddress(); - const deviceId = this.readUInt8(); - const sinkList = this.readEmberGpSinkList(); - const assignedAlias = this.readUInt16(); - const groupcastRadius = this.readUInt8(); - const securityOptions = this.readUInt8(); - const gpdSecurityFrameCounter = this.readUInt32(); - const gpdKey = this.readEmberKeyData(); - - return { - status, - options, - gpd, - deviceId, - sinkList, - assignedAlias, - groupcastRadius, - securityOptions, - gpdSecurityFrameCounter, - gpdKey, - }; - } - - public writeEmberGpSinkTableEntry(value: EmberGpSinkTableEntry): void { - this.writeUInt8(value.status); - this.writeUInt16(value.options); - this.writeEmberGpAddress(value.gpd); - this.writeUInt8(value.deviceId); - this.writeEmberGpSinkList(value.sinkList); - this.writeUInt16(value.assignedAlias); - this.writeUInt8(value.groupcastRadius); - this.writeUInt8(value.securityOptions); - this.writeUInt32(value.gpdSecurityFrameCounter); - this.writeEmberKeyData(value.gpdKey); - } - - public writeEmberDutyCycleLimits(limits: EmberDutyCycleLimits): void { - this.writeUInt16(limits.limitThresh); - this.writeUInt16(limits.critThresh); - this.writeUInt16(limits.suspLimit); - } - - public readEmberDutyCycleLimits(): EmberDutyCycleLimits { - const limitThresh = this.readUInt16(); - const critThresh = this.readUInt16(); - const suspLimit = this.readUInt16(); - - return { - limitThresh, - critThresh, - suspLimit, - }; - } - - public writeEmberPerDeviceDutyCycle(maxDevices: number, arrayOfDeviceDutyCycles: EmberPerDeviceDutyCycle[]): void { - this.writeUInt16(maxDevices); - - for (let i = 0; i < maxDevices; i++) { - this.writeUInt16(arrayOfDeviceDutyCycles[i].nodeId); - this.writeUInt16(arrayOfDeviceDutyCycles[i].dutyCycleConsumed); - } - } - - public readEmberPerDeviceDutyCycle(): EmberPerDeviceDutyCycle[] { - const maxDevices = this.readUInt8(); - const arrayOfDeviceDutyCycles: EmberPerDeviceDutyCycle[] = []; - - for (let i = 0; i < maxDevices; i++) { - const nodeId = this.readUInt16(); - const dutyCycleConsumed = this.readUInt16(); - - arrayOfDeviceDutyCycles.push({nodeId, dutyCycleConsumed}); - } - - return arrayOfDeviceDutyCycles; - } - - public readEmberZllDeviceInfoRecord(): EmberZllDeviceInfoRecord { - const ieeeAddress = this.readIeeeAddr(); - const endpointId = this.readUInt8(); - const profileId = this.readUInt16(); - const deviceId = this.readUInt16(); - const version = this.readUInt8(); - const groupIdCount = this.readUInt8(); - - return { - ieeeAddress, - endpointId, - profileId, - deviceId, - version, - groupIdCount, - }; - } - - public readEmberZllInitialSecurityState(): EmberZllInitialSecurityState { - const bitmask = this.readUInt32(); - const keyIndex = this.readUInt8(); - const encryptionKey = this.readEmberKeyData(); - const preconfiguredKey = this.readEmberKeyData(); - - return { - bitmask, - keyIndex, - encryptionKey, - preconfiguredKey, - }; - } - - public readEmberZllAddressAssignment(): EmberZllAddressAssignment { - const nodeId = this.readUInt16(); - const freeNodeIdMin = this.readUInt16(); - const freeNodeIdMax = this.readUInt16(); - const groupIdMin = this.readUInt16(); - const groupIdMax = this.readUInt16(); - const freeGroupIdMin = this.readUInt16(); - const freeGroupIdMax = this.readUInt16(); - - return { - nodeId, - freeNodeIdMin, - freeNodeIdMax, - groupIdMin, - groupIdMax, - freeGroupIdMin, - freeGroupIdMax, - }; - } - - public writeEmberBeaconIterator(value: EmberBeaconIterator): void { - this.writeUInt8(value.beacon.channel); - this.writeUInt8(value.beacon.lqi); - this.writeUInt8(value.beacon.rssi); - this.writeUInt8(value.beacon.depth); - this.writeUInt8(value.beacon.nwkUpdateId); - this.writeUInt8(value.beacon.power); - this.writeUInt8(value.beacon.parentPriority); - this.writeUInt8(value.beacon.enhanced ? 1 : 0); - this.writeUInt8(value.beacon.permitJoin ? 1 : 0); - this.writeUInt8(value.beacon.hasCapacity ? 1 : 0); - this.writeUInt16(value.beacon.panId); - this.writeUInt16(value.beacon.sender); - this.writeListUInt8(value.beacon.extendedPanId); - this.writeUInt8(value.index); - } - - public readEmberBeaconIterator(): EmberBeaconIterator { - const channel = this.readUInt8(); - const lqi = this.readUInt8(); - const rssi = this.readUInt8(); - const depth = this.readUInt8(); - 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 panId = this.readUInt16(); - const sender = this.readUInt16(); - const extendedPanId = this.readListUInt8({length: EXTENDED_PAN_ID_SIZE}); - const index = this.readUInt8(); - - return { - beacon: { - channel, - lqi, - rssi, - depth, - nwkUpdateId, - power, - parentPriority, - enhanced, - permitJoin, - hasCapacity, - panId, - sender, - extendedPanId, - supportedKeyNegotiationMethods: 0, - extended_beacon: false, - tcConnectivity: true, - longUptime: true, - preferParent: true, - macDataPollKeepalive: true, - endDeviceKeepalive: true - }, - index, - }; - } - - public writeEmberBeaconData(value: EmberBeaconData): void { - this.writeUInt8(value.channel); - this.writeUInt8(value.lqi); - this.writeUInt8(value.rssi); - this.writeUInt8(value.depth); - this.writeUInt8(value.nwkUpdateId); - this.writeUInt8(value.power); - this.writeUInt8(value.parentPriority); - this.writeUInt8(value.enhanced ? 1 : 0); - this.writeUInt8(value.permitJoin ? 1 : 0); - this.writeUInt8(value.hasCapacity ? 1 : 0); - this.writeUInt16(value.panId); - this.writeUInt16(value.sender); - this.writeListUInt8(value.extendedPanId); - } - - public readEmberBeaconData(): EmberBeaconData { - const channel = this.readUInt8(); - const lqi = this.readUInt8(); - const rssi = this.readUInt8(); - const depth = this.readUInt8(); - 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 panId = this.readUInt16(); - const sender = this.readUInt16(); - const extendedPanId = this.readListUInt8({length: EXTENDED_PAN_ID_SIZE}); - - return { - channel, - lqi, - rssi, - depth, - nwkUpdateId, - power, - parentPriority, - enhanced, - permitJoin, - hasCapacity, - panId, - sender, - extendedPanId, - supportedKeyNegotiationMethods: 0, - extended_beacon: false, - tcConnectivity: true, - longUptime: true, - preferParent: true, - macDataPollKeepalive: true, - endDeviceKeepalive: true - }; - } - - public writeEmberTokenData(tokenData: EmberTokenData): void { - this.writeUInt32(tokenData.size); - this.writeBuffer(tokenData.data, tokenData.size); - } - - public readEmberTokenData(): EmberTokenData { - const size = this.readUInt32(); - const data = this.readBufferCopy(size); - - return {size, data}; - } - - public readEmberTokenInfo(): EmberTokenInfo { - const nvm3Key = this.readUInt32(); - const isCnt = this.readUInt8() === 1 ? true : false; - const isIdx = this.readUInt8() === 1 ? true : false; - const size = this.readUInt8(); - const arraySize = this.readUInt8(); - - return { - nvm3Key, - isCnt, - isIdx, - size, - arraySize, - }; - } - - public writeEmberTokenInfo(tokenInfo: EmberTokenInfo): void { - this.writeUInt32(tokenInfo.nvm3Key); - this.writeUInt8(tokenInfo.isCnt ? 1 : 0); - this.writeUInt8(tokenInfo.isIdx ? 1 : 0); - this.writeUInt8(tokenInfo.size); - this.writeUInt8(tokenInfo.arraySize); - } -} diff --git a/src/adapter/ember/ezsp/consts.ts b/src/adapter/ember/ezsp/consts.ts deleted file mode 100644 index ba22cb11b0a..00000000000 --- a/src/adapter/ember/ezsp/consts.ts +++ /dev/null @@ -1,148 +0,0 @@ -//------------------------------------------------------------------------------------------------- -// EZSP Protocol - -/** Latest EZSP protocol version */ -export const EZSP_PROTOCOL_VERSION = 0x0D; - -/** EZSP max length + Frame Control extra byte + Frame ID extra byte */ -export const EZSP_MAX_FRAME_LENGTH = (200 + 1 + 1); - -/** EZSP Sequence Index for both legacy and extended frame format */ -export const EZSP_SEQUENCE_INDEX = 0; - -/** Legacy EZSP Frame Format */ -export const EZSP_MIN_FRAME_LENGTH = 3; -/** Legacy EZSP Frame Format */ -export const EZSP_FRAME_CONTROL_INDEX = 1; -/** Legacy EZSP Frame Format */ -export const EZSP_FRAME_ID_INDEX = 2; -/** Legacy EZSP Frame Format */ -export const EZSP_PARAMETERS_INDEX = 3; - -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_MIN_FRAME_LENGTH = 5; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_FRAME_ID_LENGTH = 2; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX = 1; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX = 2; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_FRAME_ID_LB_INDEX = 3; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_FRAME_ID_HB_INDEX = 4; -/** Extended EZSP Frame Format */ -export const EZSP_EXTENDED_PARAMETERS_INDEX = 5; - -export const EZSP_STACK_TYPE_MESH = 0x02; - - -//---- Frame Control Lower Byte (LB) Definitions - -/** - * The high bit of the frame control lower byte indicates the direction of the message. - * Commands are sent from the Host to the EM260. Responses are sent from the EM260 to the Host. - */ -export const EZSP_FRAME_CONTROL_DIRECTION_MASK = 0x80; -export const EZSP_FRAME_CONTROL_COMMAND = 0x00; -export const EZSP_FRAME_CONTROL_RESPONSE = 0x80; - -/** Bits 5 and 6 of the frame control lower byte carry the network index the ezsp message is related to. - * The NCP upon processing an incoming EZSP command, temporary switches the current network to the one indicated in the EZSP frame control. - */ -export const EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK = 0x60; -export const EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET = 5; - -// Command Frame Control Fields - -/** The EM260 enters the sleep mode specified by the command frame control once it has sent its response. */ -export const EZSP_FRAME_CONTROL_SLEEP_MODE_MASK = 0x03; - -// Response Frame Control Fields - -/** - * The overflow flag in the response frame control indicates to the Host that one or more callbacks occurred since the previous response - * and there was not enough memory available to report them to the Host. - */ -export const EZSP_FRAME_CONTROL_OVERFLOW_MASK = 0x01; -export const EZSP_FRAME_CONTROL_NO_OVERFLOW = 0x00; -export const EZSP_FRAME_CONTROL_OVERFLOW = 0x01; - -/** - * The truncated flag in the response frame control indicates to the Host that the response has been truncated. - * This will happen if there is not enough memory available to complete the response or if the response - * would have exceeded the maximum EZSP frame length. - */ -export const EZSP_FRAME_CONTROL_TRUNCATED_MASK = 0x02; -export const EZSP_FRAME_CONTROL_NOT_TRUNCATED = 0x00; -export const EZSP_FRAME_CONTROL_TRUNCATED = 0x02; - -/** - * The pending callbacks flag in the response frame control lower byte indicates to the Host that there is at least one callback ready to be read. - * This flag is clear if the response to a callback command read the last pending callback. - */ -export const EZSP_FRAME_CONTROL_PENDING_CB_MASK = 0x04; -export const EZSP_FRAME_CONTROL_PENDING_CB = 0x04; -export const EZSP_FRAME_CONTROL_NO_PENDING_CB = 0x00; - -/** The synchronous callback flag in the response frame control lower byte indicates this ezsp frame is the response to an ezspCallback(). */ -export const EZSP_FRAME_CONTROL_SYNCH_CB_MASK = 0x08; -export const EZSP_FRAME_CONTROL_SYNCH_CB = 0x08; -export const EZSP_FRAME_CONTROL_NOT_SYNCH_CB = 0x00; - -/** - * The asynchronous callback flag in the response frame control lower byte indicates this ezsp frame is a callback sent asynchronously by the ncp. - * This flag may be set only in the uart version when EZSP_VALUE_UART_SYNCH_CALLBACKS is 0. - */ -export const EZSP_FRAME_CONTROL_ASYNCH_CB_MASK = 0x10; -export const EZSP_FRAME_CONTROL_ASYNCH_CB = 0x10; -export const EZSP_FRAME_CONTROL_NOT_ASYNCH_CB = 0x00; - -//---- Frame Control Higher Byte (HB) Definitions - -/** Bit 7 of the frame control higher byte indicates whether security is enabled or not. */ -export const EZSP_EXTENDED_FRAME_CONTROL_SECURITY_MASK = 0x80; -export const EZSP_EXTENDED_FRAME_CONTROL_SECURE = 0x80; -export const EZSP_EXTENDED_FRAME_CONTROL_UNSECURE = 0x00; - -/** Bit 6 of the frame control higher byte indicates whether padding is enabled or not. */ -export const EZSP_EXTENDED_FRAME_CONTROL_PADDING_MASK = 0x40; -export const EZSP_EXTENDED_FRAME_CONTROL_PADDED = 0x40; -export const EZSP_EXTENDED_FRAME_CONTROL_UNPADDED = 0x00; - -/** Bits 0 and 1 of the frame control higher byte indicates the frame format version. */ -export const EZSP_EXTENDED_FRAME_FORMAT_VERSION_MASK = 0x03; -export const EZSP_EXTENDED_FRAME_FORMAT_VERSION = 0x01; - -/** Reserved bits 2-5 */ -export const EZSP_EXTENDED_FRAME_CONTROL_RESERVED_MASK = 0x3C; - -//------------------------------------------------------------------------------------------------- -// EZSP Data types - -/** Size of EUI64 (an IEEE address) in bytes (8). */ -export const EUI64_SIZE = 8; -/** Size of an extended PAN identifier in bytes (8). */ -export const EXTENDED_PAN_ID_SIZE = 8; -/** Size of an encryption key in bytes (16). */ -export const EMBER_ENCRYPTION_KEY_SIZE = 16; -/** Size of Implicit Certificates used for Certificate-based Key Exchange(CBKE). */ -export const EMBER_CERTIFICATE_SIZE = 48; -/** Size of Public Keys used in Elliptical Cryptography ECMQV algorithms. */ -export const EMBER_PUBLIC_KEY_SIZE = 22; -/** Size of Private Keys used in Elliptical Cryptography ECMQV algorithms. */ -export const EMBER_PRIVATE_KEY_SIZE = 21; -/** Size of the SMAC used in Elliptical Cryptography ECMQV algorithms. */ -export const EMBER_SMAC_SIZE = 16; -/** Size of the DSA signature used in Elliptical Cryptography Digital Signature Algorithms. */ -export const EMBER_SIGNATURE_SIZE = 42; -/** The size of AES-128 MMO hash is 16-bytes. This is defined in the core. ZigBee specification. */ -export const EMBER_AES_HASH_BLOCK_SIZE = 16; -/** Size of Implicit Certificates used for Certificate Based Key Exchange using the ECC283K1 curve in bytes. */ -export const EMBER_CERTIFICATE_283K1_SIZE = 74; -/** Size of Public Keys used in SECT283k1 Elliptical Cryptography ECMQV algorithms */ -export const EMBER_PUBLIC_KEY_283K1_SIZE = 37; -/** Size of Private Keys used SECT283k1 in Elliptical Cryptography ECMQV algorithms*/ -export const EMBER_PRIVATE_KEY_283K1_SIZE = 36; -/** Size of the DSA signature used in SECT283k1 Elliptical Cryptography Digital Signature Algorithms. */ -export const EMBER_SIGNATURE_283K1_SIZE = 72; diff --git a/src/adapter/ember/ezsp/enums.ts b/src/adapter/ember/ezsp/enums.ts deleted file mode 100644 index 62221847c1b..00000000000 --- a/src/adapter/ember/ezsp/enums.ts +++ /dev/null @@ -1,958 +0,0 @@ -/** EZSP Frame IDs */ -export enum EzspFrameID { - // Configuration Frames - VERSION = 0x0000, - GET_CONFIGURATION_VALUE = 0x0052, - SET_CONFIGURATION_VALUE = 0x0053, - READ_ATTRIBUTE = 0x0108, - WRITE_ATTRIBUTE = 0x0109, - ADD_ENDPOINT = 0x0002, - SET_POLICY = 0x0055, - GET_POLICY = 0x0056, - SEND_PAN_ID_UPDATE = 0x0057, - GET_VALUE = 0x00AA, - GET_EXTENDED_VALUE = 0x0003, - SET_VALUE = 0x00AB, - SET_PASSIVE_ACK_CONFIG = 0x0105, - - // Utilities Frames - NOP = 0x0005, - ECHO = 0x0081, - INVALID_COMMAND = 0x0058, - CALLBACK = 0x0006, - NO_CALLBACKS = 0x0007, - SET_TOKEN = 0x0009, - GET_TOKEN = 0x000A, - GET_MFG_TOKEN = 0x000B, - SET_MFG_TOKEN = 0x000C, - STACK_TOKEN_CHANGED_HANDLER = 0x000D, - GET_RANDOM_NUMBER = 0x0049, - SET_TIMER = 0x000E, - GET_TIMER = 0x004E, - TIMER_HANDLER = 0x000F, - DEBUG_WRITE = 0x0012, - READ_AND_CLEAR_COUNTERS = 0x0065, - READ_COUNTERS = 0x00F1, - COUNTER_ROLLOVER_HANDLER = 0x00F2, - DELAY_TEST = 0x009D, - GET_LIBRARY_STATUS = 0x0001, - GET_XNCP_INFO = 0x0013, - CUSTOM_FRAME = 0x0047, - CUSTOM_FRAME_HANDLER = 0x0054, - GET_EUI64 = 0x0026, - GET_NODE_ID = 0x0027, - GET_PHY_INTERFACE_COUNT = 0x00FC, - GET_TRUE_RANDOM_ENTROPY_SOURCE = 0x004F, - - // Networking Frames - SET_MANUFACTURER_CODE = 0x0015, - SET_POWER_DESCRIPTOR = 0x0016, - NETWORK_INIT = 0x0017, - NETWORK_STATE = 0x0018, - STACK_STATUS_HANDLER = 0x0019, - START_SCAN = 0x001A, - ENERGY_SCAN_RESULT_HANDLER = 0x0048, - NETWORK_FOUND_HANDLER = 0x001B, - SCAN_COMPLETE_HANDLER = 0x001C, - UNUSED_PAN_ID_FOUND_HANDLER = 0x00D2, - FIND_UNUSED_PAN_ID = 0x00D3, - STOP_SCAN = 0x001D, - FORM_NETWORK = 0x001E, - JOIN_NETWORK = 0x001F, - JOIN_NETWORK_DIRECTLY = 0x003B, - LEAVE_NETWORK = 0x0020, - FIND_AND_REJOIN_NETWORK = 0x0021, - PERMIT_JOINING = 0x0022, - CHILD_JOIN_HANDLER = 0x0023, - ENERGY_SCAN_REQUEST = 0x009C, - GET_NETWORK_PARAMETERS = 0x0028, - GET_RADIO_PARAMETERS = 0x00FD, - GET_PARENT_CHILD_PARAMETERS = 0x0029, - GET_CHILD_DATA = 0x004A, - SET_CHILD_DATA = 0x00AC, - CHILD_ID = 0x0106, - CHILD_INDEX = 0x0107, - GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE = 0x00C3, - GET_SOURCE_ROUTE_TABLE_FILLED_SIZE = 0x00C2, - GET_SOURCE_ROUTE_TABLE_ENTRY = 0x00C1, - GET_NEIGHBOR = 0x0079, - GET_NEIGHBOR_FRAME_COUNTER = 0x003E, - SET_NEIGHBOR_FRAME_COUNTER = 0x00AD, - SET_ROUTING_SHORTCUT_THRESHOLD = 0x00D0, - GET_ROUTING_SHORTCUT_THRESHOLD = 0x00D1, - NEIGHBOR_COUNT = 0x007A, - GET_ROUTE_TABLE_ENTRY = 0x007B, - SET_RADIO_POWER = 0x0099, - SET_RADIO_CHANNEL = 0x009A, - GET_RADIO_CHANNEL = 0x00FF, - SET_RADIO_IEEE802154_CCA_MODE = 0x0095, - SET_CONCENTRATOR = 0x0010, - SET_BROKEN_ROUTE_ERROR_CODE = 0x0011, - MULTI_PHY_START = 0x00F8, - MULTI_PHY_STOP = 0x00F9, - MULTI_PHY_SET_RADIO_POWER = 0x00FA, - SEND_LINK_POWER_DELTA_REQUEST = 0x00F7, - MULTI_PHY_SET_RADIO_CHANNEL = 0x00FB, - GET_DUTY_CYCLE_STATE = 0x0035, - SET_DUTY_CYCLE_LIMITS_IN_STACK = 0x0040, - GET_DUTY_CYCLE_LIMITS = 0x004B, - GET_CURRENT_DUTY_CYCLE = 0x004C, - DUTY_CYCLE_HANDLER = 0x004D, - GET_FIRST_BEACON = 0x003D, - GET_NEXT_BEACON = 0x0004, - GET_NUM_STORED_BEACONS = 0x0008, - CLEAR_STORED_BEACONS = 0x003C, - SET_LOGICAL_AND_RADIO_CHANNEL = 0x00B9, - - // Binding Frames - CLEAR_BINDING_TABLE = 0x002A, - SET_BINDING = 0x002B, - GET_BINDING = 0x002C, - DELETE_BINDING = 0x002D, - BINDING_IS_ACTIVE = 0x002E, - GET_BINDING_REMOTE_NODE_ID = 0x002F, - SET_BINDING_REMOTE_NODE_ID = 0x0030, - REMOTE_SET_BINDING_HANDLER = 0x0031, - REMOTE_DELETE_BINDING_HANDLER = 0x0032, - - // Messaging Frames - MAXIMUM_PAYLOAD_LENGTH = 0x0033, - SEND_UNICAST = 0x0034, - SEND_BROADCAST = 0x0036, - PROXY_BROADCAST = 0x0037, - SEND_MULTICAST = 0x0038, - SEND_MULTICAST_WITH_ALIAS = 0x003A, - SEND_REPLY = 0x0039, - MESSAGE_SENT_HANDLER = 0x003F, - SEND_MANY_TO_ONE_ROUTE_REQUEST = 0x0041, - POLL_FOR_DATA = 0x0042, - POLL_COMPLETE_HANDLER = 0x0043, - POLL_HANDLER = 0x0044, - INCOMING_SENDER_EUI64_HANDLER = 0x0062, - INCOMING_MESSAGE_HANDLER = 0x0045, - SET_SOURCE_ROUTE_DISCOVERY_MODE = 0x005A, - INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER = 0x007D, - INCOMING_ROUTE_ERROR_HANDLER = 0x0080, - INCOMING_NETWORK_STATUS_HANDLER = 0x00C4, - INCOMING_ROUTE_RECORD_HANDLER = 0x0059, - 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, - SET_EXTENDED_TIMEOUT = 0x007E, - GET_EXTENDED_TIMEOUT = 0x007F, - REPLACE_ADDRESS_TABLE_ENTRY = 0x0082, - LOOKUP_NODE_ID_BY_EUI64 = 0x0060, - LOOKUP_EUI64_BY_NODE_ID = 0x0061, - GET_MULTICAST_TABLE_ENTRY = 0x0063, - SET_MULTICAST_TABLE_ENTRY = 0x0064, - ID_CONFLICT_HANDLER = 0x007C, - WRITE_NODE_DATA = 0x00FE, - SEND_RAW_MESSAGE = 0x0096, - SEND_RAW_MESSAGE_EXTENDED = 0x0051, - MAC_PASSTHROUGH_MESSAGE_HANDLER = 0x0097, - MAC_FILTER_MATCH_MESSAGE_HANDLER = 0x0046, - RAW_TRANSMIT_COMPLETE_HANDLER = 0x0098, - SET_MAC_POLL_FAILURE_WAIT_TIME = 0x00F4, - SET_BEACON_CLASSIFICATION_PARAMS = 0x00EF, - GET_BEACON_CLASSIFICATION_PARAMS = 0x00F3, - - // Security Frames - SET_INITIAL_SECURITY_STATE = 0x0068, - GET_CURRENT_SECURITY_STATE = 0x0069, - EXPORT_KEY = 0x0114, - IMPORT_KEY = 0x0115, - SWITCH_NETWORK_KEY_HANDLER = 0x006e, - FIND_KEY_TABLE_ENTRY = 0x0075, - SEND_TRUST_CENTER_LINK_KEY = 0x0067, - ERASE_KEY_TABLE_ENTRY = 0x0076, - CLEAR_KEY_TABLE = 0x00B1, - REQUEST_LINK_KEY = 0x0014, - UPDATE_TC_LINK_KEY = 0x006C, - ZIGBEE_KEY_ESTABLISHMENT_HANDLER = 0x009B, - CLEAR_TRANSIENT_LINK_KEYS = 0x006B, - GET_NETWORK_KEY_INFO = 0x0116, - GET_APS_KEY_INFO = 0x010C, - IMPORT_LINK_KEY = 0x010E, - EXPORT_LINK_KEY_BY_INDEX = 0x010F, - EXPORT_LINK_KEY_BY_EUI = 0x010D, - CHECK_KEY_CONTEXT = 0x0110, - IMPORT_TRANSIENT_KEY = 0x0111, - EXPORT_TRANSIENT_KEY_BY_INDEX = 0x0112, - EXPORT_TRANSIENT_KEY_BY_EUI = 0x0113, - - // Trust Center Frames - TRUST_CENTER_JOIN_HANDLER = 0x0024, - BROADCAST_NEXT_NETWORK_KEY = 0x0073, - BROADCAST_NETWORK_KEY_SWITCH = 0x0074, - AES_MMO_HASH = 0x006F, - REMOVE_DEVICE = 0x00A8, - UNICAST_NWK_KEY_UPDATE = 0x00A9, - - // Certificate Based Key Exchange (CBKE) Frames - GENERATE_CBKE_KEYS = 0x00A4, - GENERATE_CBKE_KEYS_HANDLER = 0x009E, - CALCULATE_SMACS = 0x009F, - CALCULATE_SMACS_HANDLER = 0x00A0, - GENERATE_CBKE_KEYS283K1 = 0x00E8, - GENERATE_CBKE_KEYS_HANDLER283K1 = 0x00E9, - CALCULATE_SMACS283K1 = 0x00EA, - CALCULATE_SMACS_HANDLER283K1 = 0x00EB, - CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY = 0x00A1, - CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1 = 0x00EE, - GET_CERTIFICATE = 0x00A5, - GET_CERTIFICATE283K1 = 0x00EC, - DSA_SIGN = 0x00A6, - DSA_SIGN_HANDLER = 0x00A7, - DSA_VERIFY = 0x00A3, - DSA_VERIFY_HANDLER = 0x0078, - DSA_VERIFY283K1 = 0x00B0, - SET_PREINSTALLED_CBKE_DATA = 0x00A2, - 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_RX_HANDLER = 0x008e, - - // Bootloader Frames - LAUNCH_STANDALONE_BOOTLOADER = 0x008f, - SEND_BOOTLOAD_MESSAGE = 0x0090, - GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY = 0x0091, - INCOMING_BOOTLOAD_MESSAGE_HANDLER = 0x0092, - BOOTLOAD_TRANSMIT_COMPLETE_HANDLER = 0x0093, - AES_ENCRYPT = 0x0094, - - // ZLL Frames - ZLL_NETWORK_OPS = 0x00B2, - ZLL_SET_INITIAL_SECURITY_STATE = 0x00B3, - ZLL_SET_SECURITY_STATE_WITHOUT_KEY = 0x00CF, - ZLL_START_SCAN = 0x00B4, - ZLL_SET_RX_ON_WHEN_IDLE = 0x00B5, - ZLL_NETWORK_FOUND_HANDLER = 0x00B6, - ZLL_SCAN_COMPLETE_HANDLER = 0x00B7, - ZLL_ADDRESS_ASSIGNMENT_HANDLER = 0x00B8, - ZLL_TOUCH_LINK_TARGET_HANDLER = 0x00BB, - ZLL_GET_TOKENS = 0x00BC, - ZLL_SET_DATA_TOKEN = 0x00BD, - ZLL_SET_NON_ZLL_NETWORK = 0x00BF, - IS_ZLL_NETWORK = 0x00BE, - ZLL_SET_RADIO_IDLE_MODE = 0x00D4, - ZLL_GET_RADIO_IDLE_MODE = 0x00BA, - SET_ZLL_NODE_TYPE = 0x00D5, - SET_ZLL_ADDITIONAL_STATE = 0x00D6, - ZLL_OPERATION_IN_PROGRESS = 0x00D7, - ZLL_RX_ON_WHEN_IDLE_GET_ACTIVE = 0x00D8, - ZLL_SCANNING_COMPLETE = 0x00F6, - GET_ZLL_PRIMARY_CHANNEL_MASK = 0x00D9, - GET_ZLL_SECONDARY_CHANNEL_MASK = 0x00DA, - SET_ZLL_PRIMARY_CHANNEL_MASK = 0x00DB, - SET_ZLL_SECONDARY_CHANNEL_MASK = 0x00DC, - 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, - - // Green Power Frames - GP_PROXY_TABLE_PROCESS_GP_PAIRING = 0x00C9, - D_GP_SEND = 0x00C6, - D_GP_SENT_HANDLER = 0x00C7, - GPEP_INCOMING_MESSAGE_HANDLER = 0x00C5, - GP_PROXY_TABLE_GET_ENTRY = 0x00C8, - GP_PROXY_TABLE_LOOKUP = 0x00C0, - GP_SINK_TABLE_GET_ENTRY = 0x00DD, - GP_SINK_TABLE_LOOKUP = 0x00DE, - GP_SINK_TABLE_SET_ENTRY = 0x00DF, - GP_SINK_TABLE_REMOVE_ENTRY = 0x00E0, - GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY = 0x00E1, - GP_SINK_TABLE_CLEAR_ALL = 0x00E2, - GP_SINK_TABLE_INIT = 0x0070, - GP_SINK_TABLE_SET_SECURITY_FRAME_COUNTER = 0x00F5, - GP_SINK_COMMISSION = 0x010A, - GP_TRANSLATION_TABLE_CLEAR = 0x010B, - GP_SINK_TABLE_GET_NUMBER_OF_ACTIVE_ENTRIES = 0x0118, - - // Token Interface Frames - GET_TOKEN_COUNT = 0x0100, - GET_TOKEN_INFO = 0x0101, - GET_TOKEN_DATA = 0x0102, - SET_TOKEN_DATA = 0x0103, - RESET_NODE = 0x0104, - GP_SECURITY_TEST_VECTORS = 0x0117, - TOKEN_FACTORY_RESET = 0x0077 -} - -/** Identifies a configuration value. uint8_t */ -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. - */ - PACKET_BUFFER_COUNT = 0x01, - /** - * The maximum number of router neighbors the stack can keep track of. A - * neighbor is a node within radio range. - */ - NEIGHBOR_TABLE_SIZE = 0x02, - /** - * The maximum number of APS retried messages the stack can be transmitting at - * any time. - */ - APS_UNICAST_MESSAGE_COUNT = 0x03, - /** - * The maximum number of non-volatile bindings supported by the stack. - */ - BINDING_TABLE_SIZE = 0x04, - /** - * The maximum number of EUI64 to network address associations that the stack - * can maintain for the application. (Note, the total number of such address - * associations maintained by the NCP is the sum of the value of this setting - * and the value of ::TRUST_CENTER_ADDRESS_CACHE_SIZE. - */ - ADDRESS_TABLE_SIZE = 0x05, - /** - * The maximum number of multicast groups that the device may be a member of. - */ - MULTICAST_TABLE_SIZE = 0x06, - /** - * The maximum number of destinations to which a node can route messages. This - * includes both messages originating at this node and those relayed for - * others. - */ - ROUTE_TABLE_SIZE = 0x07, - /** - * The number of simultaneous route discoveries that a node will support. - */ - DISCOVERY_TABLE_SIZE = 0x08, - // 0x0A? - // 0x0B? - /** - * Specifies the stack profile. - */ - STACK_PROFILE = 0x0C, - /** - * The security level used for security at the MAC and network layers. The - * supported values are 0 (no security) and 5 (payload is encrypted and a - * four-byte MIC is used for authentication). - */ - SECURITY_LEVEL = 0x0D, - // 0x0E? - // 0x0F? - /** - * The maximum number of hops for a message. - */ - MAX_HOPS = 0x10, - /** - * The maximum number of end device children that a router will support. - */ - MAX_END_DEVICE_CHILDREN = 0x11, - /** - * The maximum amount of time that the MAC will hold a message for indirect - * transmission to a child. - */ - INDIRECT_TRANSMISSION_TIMEOUT = 0x12, - /** - * The maximum amount of time that an end device child can wait between polls. - * If no poll is heard within this timeout, then the parent removes the end - * device from its tables. Value range 0-14. The timeout corresponding to a - * value of zero is 10 seconds. The timeout corresponding to a nonzero value N - * is 2^N minutes, ranging from 2^1 = 2 minutes to 2^14 = 16384 minutes. - */ - END_DEVICE_POLL_TIMEOUT = 0x13, - // 0x14? - // 0x15? - // 0x16? - /** - * Enables boost power mode and/or the alternate transmitter output. - */ - TX_POWER_MODE = 0x17, - /** - * 0: Allow this node to relay messages. 1: Prevent this node from relaying - * messages. - */ - DISABLE_RELAY = 0x18, - /** - * The maximum number of EUI64 to network address associations that the Trust - * Center can maintain. These address cache entries are reserved for and - * reused by the Trust Center when processing device join/rejoin - * authentications. This cache size limits the number of overlapping joins the - * Trust Center can process within a narrow time window (e.g. two seconds), - * and thus should be set to the maximum number of near simultaneous joins the - * Trust Center is expected to accommodate. (Note, the total number of such - * address associations maintained by the NCP is the sum of the value of this - * setting and the value of ::ADDRESS_TABLE_SIZE.) - */ - TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19, - /** - * The size of the source route table. - */ - SOURCE_ROUTE_TABLE_SIZE = 0x1A, - // 0x1B? - /** The number of blocks of a fragmented message that can be sent in a single window. */ - FRAGMENT_WINDOW_SIZE = 0x1C, - /** The time the stack will wait (in milliseconds) between sending blocks of a fragmented message. */ - FRAGMENT_DELAY_MS = 0x1D, - /** - * The size of the Key Table used for storing individual link keys (if the - * device is a Trust Center) or Application Link Keys (if the device is a normal node). - */ - KEY_TABLE_SIZE = 0x1E, - /** The APS ACK timeout value. The stack waits this amount of time between resends of APS retried messages. */ - APS_ACK_TIMEOUT = 0x1F, - /** - * The duration of a beacon jitter, in the units used by the 15.4 scan - * parameter (((1 << duration) + 1) * 15ms), when responding to a beacon request. - */ - BEACON_JITTER_DURATION = 0x20, - // 0x21? - /** The number of PAN id conflict reports that must be received by the network manager within one minute to trigger a PAN id change. */ - PAN_ID_CONFLICT_REPORT_THRESHOLD = 0x22, - /** - * The timeout value in minutes for how long the Trust Center or a normal node - * waits for the ZigBee Request Key to complete. On the Trust Center this - * controls whether or not the device buffers the request, waiting for a - * matching pair of ZigBee Request Key. If the value is non-zero, the Trust - * Center buffers and waits for that amount of time. If the value is zero, the - * Trust Center does not buffer the request and immediately responds to the - * request. Zero is the most compliant behavior. - */ - REQUEST_KEY_TIMEOUT = 0x24, - // 0x25? - // 0x26? - // 0x27? - // 0x28? - /** - * This value indicates the size of the runtime modifiable certificate table. - * Normally certificates are stored in MFG tokens but this table can be used - * to field upgrade devices with new Smart Energy certificates. This value - * cannot be set, it can only be queried. - */ - CERTIFICATE_TABLE_SIZE = 0x29, - /** - * This is a bitmask that controls which incoming ZDO request messages are - * passed to the application. The bits are defined in the - * EmberZdoConfigurationFlags enumeration. To see if the application is - * required to send a ZDO response in reply to an incoming message, the - * application must check the APS options bitfield within the - * incomingMessageHandler callback to see if the - * EMBER_APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set. - */ - APPLICATION_ZDO_FLAGS = 0x2A, - /** The maximum number of broadcasts during a single broadcast timeout period. */ - BROADCAST_TABLE_SIZE = 0x2B, - /** The size of the MAC filter list table. */ - MAC_FILTER_TABLE_SIZE = 0x2C, - /** The number of supported networks. */ - SUPPORTED_NETWORKS = 0x2D, - /** - * Whether multicasts are sent to the RxOnWhenIdle=true address (0xFFFD) or - * the sleepy broadcast address (0xFFFF). The RxOnWhenIdle=true address is the - * ZigBee compliant destination for multicasts. - */ - SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E, - /** ZLL group address initial configuration. */ - ZLL_GROUP_ADDRESSES = 0x2F, - /** ZLL rssi threshold initial configuration. */ - ZLL_RSSI_THRESHOLD = 0x30, - // 0x31? - // 0x32? - /** Toggles the MTORR flow control in the stack. */ - MTORR_FLOW_CONTROL = 0x33, - /** Setting the retry queue size. Applies to all queues. Default value in the sample applications is 16. */ - RETRY_QUEUE_SIZE = 0x34, - /** - * Setting the new broadcast entry threshold. The number (BROADCAST_TABLE_SIZE - * - NEW_BROADCAST_ENTRY_THRESHOLD) of broadcast table entries are reserved - * for relaying the broadcast messages originated on other devices. The local - * device will fail to originate a broadcast message after this threshold is - * reached. Setting this value to BROADCAST_TABLE_SIZE and greater will - * effectively kill this limitation. - */ - NEW_BROADCAST_ENTRY_THRESHOLD = 0x35, - /** - * The length of time, in seconds, that a trust center will store a transient - * link key that a device can use to join its network. A transient key is - * added with a call to emberAddTransientLinkKey. After the transient key is - * added, it will be removed once this amount of time has passed. A joining - * device will not be able to use that key to join until it is added again on - * the trust center. The default value is 300 seconds, i.e., 5 minutes. - */ - TRANSIENT_KEY_TIMEOUT_S = 0x36, - /** The number of passive acknowledgements to record from neighbors before we stop re-transmitting broadcasts */ - BROADCAST_MIN_ACKS_NEEDED = 0x37, - /** - * The length of time, in seconds, that a trust center will allow a Trust - * Center (insecure) rejoin for a device that is using the well-known link - * key. This timeout takes effect once rejoins using the well-known key has - * been allowed. This command updates the - * sli_zigbee_allow_tc_rejoins_using_well_known_key_timeout_sec value. - */ - TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38, - /** Valid range of a CTUNE value is 0x0000-0x01FF. Higher order bits (0xFE00) of the 16-bit value are ignored. */ - CTUNE_VALUE = 0x39, - // 0x3A? - // 0x3B? - // 0x3C? - // 0x3D? - // 0x3E? - // 0x3F? - /** - * To configure non trust center node to assume a concentrator type of the - * trust center it join to, until it receive many-to-one route request from - * the trust center. For the trust center node, concentrator type is - * configured from the concentrator plugin. The stack by default assumes trust - * center be a low RAM concentrator that make other devices send route record - * to the trust center even without receiving a many-to-one route request. The - * default concentrator type can be changed by setting appropriate - * EmberAssumeTrustCenterConcentratorType config value. - */ - ASSUME_TC_CONCENTRATOR_TYPE = 0x40, - /** 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 -} - -/** Identifies a policy decision. */ -export enum EzspDecisionId { - /** - * BINDING_MODIFICATION_POLICY default decision. - * - * Do not allow the local binding table to be changed by remote nodes. - */ - DISALLOW_BINDING_MODIFICATION = 0x10, - /** - * BINDING_MODIFICATION_POLICY decision. - * - * Allow remote nodes to change the local binding table. - */ - ALLOW_BINDING_MODIFICATION = 0x11, - /** - * BINDING_MODIFICATION_POLICY decision. - * - * Allows remote nodes to set local binding entries only if the entries correspond to endpoints - * defined on the device, and for output clusters bound to those endpoints. - */ - CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS = 0x12, - /** - * UNICAST_REPLIES_POLICY default decision. - * - * The NCP will automatically send an empty reply (containing no payload) for every unicast received. - * */ - HOST_WILL_NOT_SUPPLY_REPLY = 0x20, - /** - * UNICAST_REPLIES_POLICY decision. - * - * The NCP will only send a reply if it receives a sendReply command from the Host. - */ - HOST_WILL_SUPPLY_REPLY = 0x21, - /** - * POLL_HANDLER_POLICY default decision. - * - * Do not inform the Host when a child polls. - */ - POLL_HANDLER_IGNORE = 0x30, - /** - * POLL_HANDLER_POLICY decision. - * - * Generate a pollHandler callback when a child polls. - */ - POLL_HANDLER_CALLBACK = 0x31, - /** - * MESSAGE_CONTENTS_IN_CALLBACK_POLICY default decision. - * - * Include only the message tag in the messageSentHandler callback. - */ - MESSAGE_TAG_ONLY_IN_CALLBACK = 0x40, - /** - * MESSAGE_CONTENTS_IN_CALLBACK_POLICY decision. - * - * Include both the message tag and the message contents in the messageSentHandler callback. - */ - MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK = 0x41, - /** - * TC_KEY_REQUEST_POLICY decision. - * - * When the Trust Center receives a request for a Trust Center link key, it will be ignored. - */ - DENY_TC_KEY_REQUESTS = 0x50, - /** - * TC_KEY_REQUEST_POLICY decision. - * - * When the Trust Center receives a request for a Trust Center link key, it will reply to it with the corresponding key. - */ - ALLOW_TC_KEY_REQUESTS_AND_SEND_CURRENT_KEY = 0x51, - /** - * TC_KEY_REQUEST_POLICY decision. - * - * When the Trust Center receives a request for a Trust Center link key, it will generate a key to send to the joiner. - * After generation, the key will be added to the transient key tabe and After verification, this key will be added into the link key table. - */ - ALLOW_TC_KEY_REQUEST_AND_GENERATE_NEW_KEY = 0x52, - /** - * APP_KEY_REQUEST_POLICY decision. - * When the Trust Center receives a request for an application link key, it will be ignored. - * */ - DENY_APP_KEY_REQUESTS = 0x60, - /** - * APP_KEY_REQUEST_POLICY decision. - * - * When the Trust Center receives a request for an application link key, it will randomly generate a key and send it to both partners. - */ - ALLOW_APP_KEY_REQUESTS = 0x61, - /** Indicates that packet validate library checks are enabled on the NCP. */ - PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62, - /** Indicates that packet validate library checks are NOT enabled on the NCP. */ - PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 -} - -/** - * This is the policy decision bitmask that controls the trust center decision strategies. - * The bitmask is modified and extracted from the EzspDecisionId for supporting bitmask operations. - * uint16_t - */ -export enum EzspDecisionBitmask { - /** Disallow joins and rejoins. */ - DEFAULT_CONFIGURATION = 0x0000, - /** Send the network key to all joining devices. */ - ALLOW_JOINS = 0x0001, - /** Send the network key to all rejoining devices. */ - ALLOW_UNSECURED_REJOINS = 0x0002, - /** Send the network key in the clear. */ - SEND_KEY_IN_CLEAR = 0x0004, - /** Do nothing for unsecured rejoins. */ - IGNORE_UNSECURED_REJOINS = 0x0008, - /** Allow joins if there is an entry in the transient key table. */ - JOINS_USE_INSTALL_CODE_KEY = 0x0010, - /** Delay sending the network key to a new joining device. */ - DEFER_JOINS = 0x0020, -} - -/** Identifies a policy. */ -export enum EzspPolicyId { - /** Controls trust center behavior. */ - TRUST_CENTER_POLICY = 0x00, - /** Controls how external binding modification requests are handled. */ - BINDING_MODIFICATION_POLICY = 0x01, - /** Controls whether the Host supplies unicast replies. */ - UNICAST_REPLIES_POLICY = 0x02, - /** Controls whether pollHandler callbacks are generated. */ - POLL_HANDLER_POLICY = 0x03, - /** Controls whether the message contents are included in the messageSentHandler callback. */ - MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04, - /** Controls whether the Trust Center will respond to Trust Center link key requests. */ - TC_KEY_REQUEST_POLICY = 0x05, - /** Controls whether the Trust Center will respond to application link key requests. */ - APP_KEY_REQUEST_POLICY = 0x06, - /** - * Controls whether ZigBee packets that appear invalid are automatically dropped by the stack. - * A counter will be incremented when this occurs. - */ - PACKET_VALIDATE_LIBRARY_POLICY = 0x07, - /** Controls whether the stack will process ZLL messages. */ - ZLL_POLICY = 0x08, - /** - * Controls whether Trust Center (insecure) rejoins for devices using the well-known link key are accepted. - * If rejoining using the well-known key is allowed, - * it is disabled again after sli_zigbee_allow_tc_rejoins_using_well_known_key_timeout_sec seconds. - */ - TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 -} - -/** Identifies a value. */ -export enum EzspValueId { - /** The contents of the node data stack token. */ - TOKEN_STACK_NODE_DATA = 0x00, - /** The types of MAC passthrough messages that the host wishes to receive. */ - MAC_PASSTHROUGH_FLAGS = 0x01, - /** - * The source address used to filter legacy EmberNet messages when the - * 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, - /** Selects sending synchronous callbacks in ezsp-uart. */ - UART_SYNCH_CALLBACKS = 0x04, - /** - * The maximum incoming transfer size for the local node. - * Default value is set to 82 and does not use fragmentation. Sets the value in Node Descriptor. - * To set, this takes the input of a uint8 array of length 2 where you pass the lower byte at index 0 and upper byte at index 1. - */ - MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05, - /** - * The maximum outgoing transfer size for the local node. - * Default value is set to 82 and does not use fragmentation. Sets the value in Node Descriptor. - * To set, this takes the input of a uint8 array of length 2 where you pass the lower byte at index 0 and upper byte at index 1. - */ - MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06, - /** A bool indicating whether stack tokens are written to persistent storage as they change. */ - STACK_TOKEN_WRITING = 0x07, - /** A read-only value indicating whether the stack is currently performing a rejoin. */ - STACK_IS_PERFORMING_REJOIN = 0x08, - /** A list of EmberMacFilterMatchData values. */ - MAC_FILTER_LIST = 0x09, - /** The Ember Extended Security Bitmask. */ - EXTENDED_SECURITY_BITMASK = 0x0A, - /** The node short ID. */ - NODE_SHORT_ID = 0x0B, - /** The descriptor capability of the local node. Write only. */ - DESCRIPTOR_CAPABILITY = 0x0C, - /** The stack device request sequence number of the local node. */ - STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D, - /** Enable or disable radio hold-off. */ - RADIO_HOLD_OFF = 0x0E, - /** The flags field associated with the endpoint data. */ - ENDPOINT_FLAGS = 0x0F, - /** Enable/disable the Mfg security config key settings. */ - 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, - /** - * 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). - * If a host initiated a rejoin the reason will be set by default to EMBER_REJOIN_DUE_TO_APP_EVENT_1. - * If the application wishes to denote its own rejoin reasons it can do so by calling - * ezspSetValue(EMBER_VALUE_HOST_REJOIN_REASON, EMBER_REJOIN_DUE_TO_APP_EVENT_X). - * X is a number corresponding to one of the app events defined. - * If the NCP initiated a rejoin it will record this value internally for retrieval by ezspGetValue(REAL_REJOIN_REASON). - */ - LAST_REJOIN_REASON = 0x13, - /** The next ZigBee sequence number. */ - NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14, - /** CCA energy detect threshold for radio. */ - CCA_THRESHOLD = 0x15, - /** The threshold value for a counter */ - SET_COUNTER_THRESHOLD = 0x17, - /** Resets all counters thresholds to 0xFF */ - RESET_COUNTER_THRESHOLDS = 0x18, - /** Clears all the counters */ - CLEAR_COUNTERS = 0x19, - /** The node's new certificate signed by the CA. */ - CERTIFICATE_283K1 = 0x1A, - /** The Certificate Authority's public key. */ - PUBLIC_KEY_283K1 = 0x1B, - /** The node's new static private key. */ - PRIVATE_KEY_283K1 = 0x1C, - // 0x1D? - // 0x1E? - // 0x1F? - // 0x20? - // 0x21? - // 0x22? - /** The NWK layer security frame counter value */ - NWK_FRAME_COUNTER = 0x23, - /** The APS layer security frame counter value. Managed by the stack. Users should not set these unless doing backup and restore. */ - APS_FRAME_COUNTER = 0x24, - /** Sets the device type to use on the next rejoin using device type */ - RETRY_DEVICE_TYPE = 0x25, - // 0x26? - // 0x27? - // 0x28? - /** Setting this byte enables R21 behavior on the NCP. */ - ENABLE_R21_BEHAVIOR = 0x29, - /** Configure the antenna mode(0-don't switch,1-primary,2-secondary,3-TX antenna diversity). */ - ANTENNA_MODE = 0x30, - /** Enable or disable packet traffic arbitration. */ - ENABLE_PTA = 0x31, - /** Set packet traffic arbitration configuration options. */ - PTA_OPTIONS = 0x32, - /** Configure manufacturing library options (0-non-CSMA transmits,1-CSMA transmits). To be used with Manufacturing Library. */ - MFGLIB_OPTIONS = 0x33, - /** - * Sets the flag to use either negotiated power by link power delta (LPD) or fixed power value provided by user - * while forming/joining a network for packet transmissions on sub-ghz interface. This is mainly for testing purposes. - */ - USE_NEGOTIATED_POWER_BY_LPD = 0x34, - /** Set packet traffic arbitration PWM options. */ - PTA_PWM_OPTIONS = 0x35, - /** Set packet traffic arbitration directional priority pulse width in microseconds. */ - PTA_DIRECTIONAL_PRIORITY_PULSE_WIDTH = 0x36, - /** Set packet traffic arbitration phy select timeout(ms). */ - PTA_PHY_SELECT_TIMEOUT = 0x37, - /** Configure the RX antenna mode: (0-do not switch; 1-primary; 2-secondary; 3-RX antenna diversity). */ - ANTENNA_RX_MODE = 0x38, - /** Configure the timeout to wait for the network key before failing a join. Acceptable timeout range [3,255]. Value is in seconds. */ - NWK_KEY_TIMEOUT = 0x39, - /** - * The number of failed CSMA attempts due to failed CCA made by the MAC before continuing transmission with CCA disabled. - * This is the same as calling the emberForceTxAfterFailedCca(uint8_t csmaAttempts) API. A value of 0 disables the feature. - */ - FORCE_TX_AFTER_FAILED_CCA_ATTEMPTS = 0x3A, - /** - * The length of time, in seconds, that a trust center will store a transient link key that a device can use to join its network. - * A transient key is added with a call to sl_zb_sec_man_import_transient_key. After the transient key is added, - * it will be removed once this amount of time has passed. A joining device will not be able to use that key to join - * until it is added again on the trust center. - * The default value is 300 seconds (5 minutes). - */ - 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. */ - MAX_BEACONS_TO_STORE = 0x3D, - /** Set the mask to filter out unacceptable child timeout options on a router. */ - END_DEVICE_TIMEOUT_OPTIONS_MASK = 0x3E, - /** The end device keep-alive mode supported by the parent. */ - END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F, - /** - * Return the active radio config. Read only. - * Values are 0: Default, 1: Antenna Diversity, 2: Co-Existence, 3: Antenna Diversity and Co-Existence. - */ - ACTIVE_RADIO_CONFIG = 0x41, - /** Return the number of seconds the network will remain open. A return value of 0 indicates that the network is closed. Read only. */ - NWK_OPEN_DURATION = 0x42, - /** - * Timeout in milliseconds to store entries in the transient device table. - * If the devices are not authenticated before the timeout, the entry shall be purged - */ - TRANSIENT_DEVICE_TIMEOUT = 0x43, - /** - * Return information about the key storage on an NCP. - * Returns 0 if keys are in classic key storage, and 1 if they are located in PSA key storage. Read only. - */ - 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 -} - -/** - * Identifies a value based on specified characteristics. - * Each set of characteristics is unique to that value and is specified during the call to get the extended value. - * - * uint16_t - */ -export enum EzspExtendedValueId { - /** The flags field associated with the specified endpoint. Value is uint16_t */ - ENDPOINT_FLAGS = 0x0000, - /** - * This is the reason for the node to leave the network as well as the device that told it to leave. - * The leave reason is the 1st byte of the value while the node ID is the 2nd and 3rd byte. - * If the leave was caused due to an API call rather than an over the air message, the node ID will be EMBER_UNKNOWN_NODE_ID (0xFFFD). - */ - LAST_LEAVE_REASON = 0x0001, - /** This number of bytes of overhead required in the network frame for source routing to a particular destination. */ - GET_SOURCE_ROUTE_OVERHEAD = 0x0002 -} - -/** Flags associated with the endpoint data configured on the NCP. */ -export enum EzspEndpointFlag { - /** Indicates that the endpoint is disabled and NOT discoverable via ZDO. */ - DISABLED = 0x00, - /** Indicates that the endpoint is enabled and discoverable via ZDO. */ - ENABLED = 0x01 -} - -/** Notes the last leave reason. uint8_t */ -export enum EmberLeaveReason { - REASON_NONE = 0, - DUE_TO_NWK_LEAVE_MESSAGE = 1, - DUE_TO_APS_REMOVE_MESSAGE = 2, - // Currently, the stack does not process the ZDO leave message since it is optional. - DUE_TO_ZDO_LEAVE_MESSAGE = 3, - DUE_TO_ZLL_TOUCHLINK = 4, - - DUE_TO_APP_EVENT_1 = 0xFF, -}; - -/** Notes the last rejoin reason. uint8_t */ -export enum EmberRejoinReason { - REASON_NONE = 0, - DUE_TO_NWK_KEY_UPDATE = 1, - DUE_TO_LEAVE_MESSAGE = 2, - DUE_TO_NO_PARENT = 3, - DUE_TO_ZLL_TOUCHLINK = 4, - DUE_TO_END_DEVICE_REBOOT = 5, - - // App. Framework events - // 0xA0 - 0xE0 - // See af.h for a subset of defined rejoin reasons - - // Customer-defined Events - // These are numbered down from 0xFF so their assigned values - // need not change if more application events are needed. - DUE_TO_APP_EVENT_5 = 0xFB, - DUE_TO_APP_EVENT_4 = 0xFC, - DUE_TO_APP_EVENT_3 = 0xFD, - DUE_TO_APP_EVENT_2 = 0xFE, - DUE_TO_APP_EVENT_1 = 0xFF, -}; - -/** Manufacturing token IDs used by ezspGetMfgToken(). */ -export enum EzspMfgTokenId { - /** Custom version (2 bytes). */ - CUSTOM_VERSION = 0x00, - /** Manufacturing string (16 bytes). */ - STRING = 0x01, - /** Board name (16 bytes). */ - BOARD_NAME = 0x02, - /** Manufacturing ID (2 bytes). */ - MANUF_ID = 0x03, - /** Radio configuration (2 bytes). */ - PHY_CONFIG = 0x04, - /** Bootload AES key (16 bytes). */ - BOOTLOAD_AES_KEY = 0x05, - /** ASH configuration (40 bytes). */ - ASH_CONFIG = 0x06, - /** EZSP storage (8 bytes). */ - EZSP_STORAGE = 0x07, - /** - * Radio calibration data (64 bytes). 4 bytes are stored for each of the 16 channels. - * This token is not stored in the Flash Information Area. It is updated by the stack each time a calibration is performed. - */ - STACK_CAL_DATA = 0x08, - /** Certificate Based Key Exchange (CBKE) data (92 bytes). */ - CBKE_DATA = 0x09, - /** Installation code (20 bytes). */ - INSTALLATION_CODE = 0x0A, - /** - * Radio channel filter calibration data (1 byte). - * This token is not stored in the Flash Information Area. It is updated by the stack each time a calibration is performed. - */ - STACK_CAL_FILTER = 0x0B, - /** Custom EUI64 MAC address (8 bytes). */ - CUSTOM_EUI_64 = 0x0C, - /** CTUNE value (2 byte). */ - CTUNE = 0x0D -} - - -export enum EzspSleepMode { - /** Processor idle. */ - IDLE = 0x00, - /** Wake on interrupt or timer. */ - DEEP_SLEEP = 0x01, - /** Wake on interrupt only. */ - POWER_DOWN = 0x02, - /** Reserved */ - RESERVED_SLEEP = 0x03, -} \ No newline at end of file diff --git a/src/adapter/ember/ezsp/ezsp.ts b/src/adapter/ember/ezsp/ezsp.ts deleted file mode 100644 index f462e7352b3..00000000000 --- a/src/adapter/ember/ezsp/ezsp.ts +++ /dev/null @@ -1,7969 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import EventEmitter from "events"; -import {SerialPortOptions} from "../../tstype"; -import Cluster from "../../../zcl/definition/cluster"; -import {byteToBits, getMacCapFlags, highByte, highLowToInt, lowByte, lowHighBits} from "../utils/math"; -import { - EmberOutgoingMessageType, - EmberCounterType, - EmberDutyCycleState, - EmberEntropySource, - EmberEventUnits, - EmberLibraryId, - EmberLibraryStatus, - EmberMultiPhyNwkConfig, - EmberNetworkStatus, - EmberNodeType, - EmberStatus, - EzspNetworkScanType, - EzspStatus, - SLStatus, - EmberIncomingMessageType, - EmberSourceRouteDiscoveryMode, - EmberMacPassthroughType, - SecManKeyType, - EmberKeyStatus, - SecManFlag, - EmberDeviceUpdate, - EmberJoinDecision, - EzspZllNetworkOperation, - EmberGpSecurityLevel, - EmberGpKeyType, - EmberTXPowerMode, - EmberExtendedSecurityBitmask, - EmberStackError, - EmberInterpanMessageType, - EmberGpApplicationId, -} from "../enums"; -import { - EmberVersion, - EmberEUI64, - EmberPanId, - EmberBeaconData, - EmberBeaconIterator, - EmberBindingTableEntry, - EmberChildData, - EmberDutyCycleLimits, - EmberMultiPhyRadioParameters, - EmberNeighborTableEntry, - EmberNetworkInitStruct, - EmberNetworkParameters, - EmberNodeId, - EmberPerDeviceDutyCycle, - EmberRouteTableEntry, - EmberApsFrame, - EmberMulticastTableEntry, - EmberBeaconClassificationParams, - EmberInitialSecurityState, - EmberCurrentSecurityState, - SecManContext, - SecManKey, - SecManNetworkKeyInfo, - SecManAPSKeyMetadata, - EmberKeyData, - EmberAesMmoHashContext, - EmberPublicKeyData, - EmberCertificateData, - EmberSmacData, - EmberPublicKey283k1Data, - EmberCertificate283k1Data, - EmberMessageDigest, - EmberSignatureData, - EmberSignature283k1Data, - EmberPrivateKeyData, - EmberZllNetwork, - EmberZllInitialSecurityState, - EmberZllDeviceInfoRecord, - EmberZllAddressAssignment, - EmberTokTypeStackZllData, - EmberTokTypeStackZllSecurity, - EmberGpAddress, - EmberGpProxyTableEntry, - EmberGpSinkTableEntry, - EmberTokenInfo, - EmberTokenData, - EmberZigbeeNetwork -} from "../types"; -import { - EmberZdoStatus, - ZDOLQITableEntry, - 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, - ZDORoutingTableEntry, - ZDO_MESSAGE_OVERHEAD, - ZDO_PROFILE_ID, - ZDOBindingTableEntry, - END_DEVICE_ANNOUNCE, - IEEEAddressResponsePayload, - NetworkAddressResponsePayload, - MatchDescriptorsResponsePayload, - SimpleDescriptorResponsePayload, - NodeDescriptorResponsePayload, - PowerDescriptorResponsePayload, - ActiveEndpointsResponsePayload, - LQITableResponsePayload, - RoutingTableResponsePayload, - BindingTableResponsePayload, - EndDeviceAnnouncePayload -} from "../zdo"; -import { - EZSP_FRAME_CONTROL_ASYNCH_CB, - EZSP_FRAME_CONTROL_INDEX, - EZSP_MAX_FRAME_LENGTH, - EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX, - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - EZSP_EXTENDED_FRAME_FORMAT_VERSION, - EZSP_EXTENDED_FRAME_ID_HB_INDEX, - EZSP_EXTENDED_FRAME_ID_LB_INDEX, - EZSP_EXTENDED_PARAMETERS_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_PARAMETERS_INDEX, - EZSP_SEQUENCE_INDEX, - EZSP_FRAME_CONTROL_DIRECTION_MASK, - EZSP_FRAME_CONTROL_RESPONSE, - EZSP_FRAME_CONTROL_TRUNCATED_MASK, - EZSP_FRAME_CONTROL_TRUNCATED, - EZSP_FRAME_CONTROL_OVERFLOW_MASK, - EZSP_FRAME_CONTROL_OVERFLOW, - EZSP_FRAME_CONTROL_PENDING_CB_MASK, - EZSP_FRAME_CONTROL_PENDING_CB, - EXTENDED_PAN_ID_SIZE, -} from "./consts"; -import { - EmberLeaveReason, - EmberRejoinReason, - EzspConfigId, - EzspEndpointFlag, - EzspExtendedValueId, - EzspFrameID, - EzspMfgTokenId, - EzspPolicyId, - EzspSleepMode, - EzspValueId -} from "./enums"; -import {AshEvents, UartAsh} from "../uart/ash"; -import {EzspBuffer} from "../uart/queues"; -import {EzspBuffalo} from "./buffalo"; -import { - GP_PROFILE_ID, - HA_PROFILE_ID, - INTERPAN_APS_FRAME_CONTROL_NO_DELIVERY_MODE, - INTERPAN_APS_FRAME_DELIVERY_MODE_MASK, - INTERPAN_APS_FRAME_SECURITY, - INTERPAN_APS_MULTICAST_SIZE, - INTERPAN_APS_UNICAST_BROADCAST_SIZE, - LONG_DEST_FRAME_CONTROL, - MAC_ACK_REQUIRED, - MIN_STUB_APS_SIZE, - SHORT_DEST_FRAME_CONTROL, - STUB_NWK_FRAME_CONTROL, - STUB_NWK_SIZE, - TOUCHLINK_PROFILE_ID, - WILDCARD_PROFILE_ID -} from "../consts"; -import {FIXED_ENDPOINTS} from "../adapter/endpoints"; - -const debug = Debug('zigbee-herdsman:adapter:ember:ezsp'); - - -/** - * Simple object to resolve/timeout on command waiting for response. - */ -type EzspWaiter = { - timer: NodeJS.Timeout, - resolve: (value: EzspStatus | PromiseLike) => void; -}; - -/** no multi-network atm, so just use const */ -const DEFAULT_NETWORK_INDEX = FIXED_ENDPOINTS[0].networkIndex; -/** other values not supported atm */ -const DEFAULT_SLEEP_MODE = EzspSleepMode.IDLE; -/** Maximum number of times we attempt to reset the NCP and start the ASH protocol. */ -const MAX_INIT_ATTEMPTS = 5; -/** - * This is the max hops that the network can support - used to determine the max source route overhead - * and broadcast radius if we havent defined MAX_HOPS then define based on profile ID - */ -// #ifdef HAS_SECURITY_PROFILE_SE -// export const ZA_MAX_HOPS = 6; -// #else -const ZA_MAX_HOPS = 12; -// #endif -/** - * The mask applied to generated message tags used by the framework when sending messages via EZSP. - * Customers who call ezspSend functions directly must use message tags outside this mask. - */ -const MESSAGE_TAG_MASK = 0x7F; - -/* eslint-disable max-len */ -export enum EzspEvents { - //-- App logic - ncpNeedsResetAndInit = 'ncpNeedsResetAndInit', - - //-- ezspIncomingMessageHandler - /** params => status: EmberZdoStatus, sender: EmberNodeId, apsFrame: EmberApsFrame, payload: { cluster-dependent @see zdo.ts } */ - ZDO_RESPONSE = 'ZDO_RESPONSE', - /** params => type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: EmberNodeId, messageContents: Buffer */ - INCOMING_MESSAGE = 'INCOMING_MESSAGE', - /** params => sourcePanId: EmberPanId, sourceAddress: EmberEUI64, groupId: number | null, lastHopLqi: number, messageContents: Buffer */ - TOUCHLINK_MESSAGE = 'TOUCHLINK_MESSAGE', - /** params => sender: EmberNodeId, apsFrame: EmberApsFrame, payload: EndDeviceAnnouncePayload */ - END_DEVICE_ANNOUNCE = 'END_DEVICE_ANNOUNCE', - - //-- ezspStackStatusHandler - /** params => status: EmberStatus */ - STACK_STATUS = 'STACK_STATUS', - - //-- ezspTrustCenterJoinHandler - /** params => newNodeId: EmberNodeId, newNodeEui64: EmberEUI64, status: EmberDeviceUpdate, policyDecision: EmberJoinDecision, parentOfNewNodeId: EmberNodeId */ - TRUST_CENTER_JOIN = 'TRUST_CENTER_JOIN', - - //-- ezspMessageSentHandler - /** params => type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number */ - // MESSAGE_SENT_SUCCESS = 'MESSAGE_SENT_SUCCESS', - /** params => type: EmberOutgoingMessageType, indexOrDestination: number, apsFrame: EmberApsFrame, messageTag: number */ - MESSAGE_SENT_DELIVERY_FAILED = 'MESSAGE_SENT_DELIVERY_FAILED', - - //-- ezspGpepIncomingMessageHandler - /** params => sender: number | EmberEUI64, gpdCommandId: number, gpdLink: number, sequenceNumber: number, deviceId?: number, options?: number, key?: EmberKeyData, counter?: number */ - GREENPOWER_MESSAGE = 'GREENPOWER_MESSAGE', -} -/* eslint-enable max-len */ - -/** - * Host EZSP layer. - * - * Provides functions that allow the Host application to send every EZSP command to the NCP. - * - * Commands to send to the serial>ASH layers all are named `ezsp${CommandName}`. - * They do nothing but build the command, send it and return the value(s). - * 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 'ncpNeedsResetAndInit(EzspStatus)' An error was detected that requires resetting the NCP. - */ -export class Ezsp extends EventEmitter { - private readonly tickInterval: number; - public readonly ash: UartAsh; - private readonly buffalo: EzspBuffalo; - /** The contents of the current EZSP frame. CAREFUL using this guy, it's pre-allocated. */ - private readonly frameContents: Buffer; - /** The total Length of the incoming frame */ - private frameLength: number; - - private initialVersionSent: boolean; - /** True if a command is in the process of being sent. */ - private sendingCommand: boolean; - /** EZSP frame sequence number. Used in EZSP_SEQUENCE_INDEX byte. */ - 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; - - /** 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) { - super(); - - this.tickInterval = tickInterval || 60; - this.frameContents = Buffer.alloc(EZSP_MAX_FRAME_LENGTH); - this.buffalo = new EzspBuffalo(this.frameContents); - - this.ash = new UartAsh(options); - this.ash.on(AshEvents.hostError, this.onAshHostError.bind(this)); - this.ash.on(AshEvents.ncpError, this.onAshNCPError.bind(this)); - this.ash.on(AshEvents.frame, this.onAshFrame.bind(this)); - } - - /** - * Returns the number of EZSP responses that have been received by the serial - * protocol and are ready to be collected by the EZSP layer via - * responseReceived(). - */ - get pendingResponseCount(): number { - return this.ash.rxQueue.length; - } - - /** - * Create a string representation of the last frame in storage (sent or received). - */ - get frameToString(): string { - const id = this.buffalo.getFrameId(); - return `[FRAME: ID=${id}:"${EzspFrameID[id]}" Seq=${this.frameContents[EZSP_SEQUENCE_INDEX]} Len=${this.frameLength}]`; - } - - private initVariables(): void { - clearInterval(this.tickHandle); - - this.frameContents.fill(0); - this.frameLength = 0; - this.buffalo.setPosition(0); - this.initialVersionSent = false; - this.sendingCommand = false; - this.frameSequence = -1;// start at 0 - this.sendSequence = 0;// start at 1 - this.waitingForResponse = false; - this.responseWaiter = null; - this.counterErrQueueFull = 0; - this.tickHandle = null; - } - - public async start(): Promise { - console.log(`======== EZSP starting ========`); - - this.initVariables(); - - let status: EzspStatus; - - for (let i = 0; i < MAX_INIT_ATTEMPTS; i++) { - status = await this.ash.resetNcp(); - - if (status !== EzspStatus.SUCCESS) { - return status; - } - - status = await this.ash.start(); - - if (status === EzspStatus.SUCCESS) { - console.log(`======== EZSP started ========`); - this.registerHandlers(); - return status; - } - } - - return status; - } - - /** - * Cleanly close down the serial protocol (UART). - * After this function has been called, init() must be called to resume communication with the NCP. - */ - public async stop(): Promise { - await this.ash.stop(); - - if (this.waitingForResponse) { - clearTimeout(this.responseWaiter.timer); - } - - clearInterval(this.tickHandle); - - this.initVariables(); - console.log(`======== EZSP stopped ========`); - } - - /** - * Check if connected. - * If not, attempt to restore the connection. - * - * @returns - */ - public checkConnection(): boolean { - return this.ash.connected; - } - - private onAshHostError(status: EzspStatus): void { - this.ezspErrorHandler(status); - } - - private onAshNCPError(status: EzspStatus): void { - this.ezspErrorHandler(status); - } - - private onAshFrame(): void { - // let tick handle if not waiting for response (CBs) - if (this.waitingForResponse) { - const status = this.responseReceived(); - - if (status !== EzspStatus.NO_RX_DATA) { - // we've got a non-CB frame, must be it! - clearTimeout(this.responseWaiter.timer); - this.responseWaiter.resolve(status); - } - } - } - - /** - * Event from the EZSP layer indicating that the transaction with the NCP could not be completed due to a - * serial protocol error or that the response received from the NCP reported an error. - * The status parameter provides more information about the error. - * - * @param status - */ - public ezspErrorHandler(status: EzspStatus): void { - const lastFrameStr = `Last: ${this.frameToString}.`; - - if (status === EzspStatus.ERROR_QUEUE_FULL) { - this.counterErrQueueFull += 1; - - console.error(`NCP Queue full (counter: ${this.counterErrQueueFull}). ${lastFrameStr}`); - } else if (status === EzspStatus.ERROR_OVERFLOW) { - console.error( - `The NCP has run out of buffers, causing general malfunction. Remediate network congestion, if present. ` - + lastFrameStr - ); - } else { - console.error(`ERROR Transaction failure; status=${EzspStatus[status]}. ${lastFrameStr}`); - } - - // 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. - // 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)) { - this.emit(EzspEvents.ncpNeedsResetAndInit, status); - } - } - - private registerHandlers(): void { - this.tickHandle = setInterval(this.tick.bind(this), this.tickInterval); - } - - /** - * The Host application must call this function periodically to allow the EZSP layer to handle asynchronous events. - */ - private tick(): void { - if (this.sendingCommand) { - // don't process any callbacks while expecting a command's response - return; - } - - // nothing in the rx queue, nothing to receive - if (this.ash.rxQueue.empty) { - return; - } - - if (this.responseReceived() === EzspStatus.SUCCESS) { - this.callbackDispatch(); - } - } - - private nextFrameSequence(): number { - return (this.frameSequence = ((++this.frameSequence) & 0xFF)); - } - - private startCommand(command: number): void { - if (this.sendingCommand) { - console.error(`[SEND COMMAND] Cannot send second one before processing response from first one.`); - throw new Error(EzspStatus[EzspStatus.ERROR_INVALID_CALL]); - } - - this.sendingCommand = true; - - // Send initial EZSP_VERSION command with old packet format for old Hosts/NCPs - if (command === EzspFrameID.VERSION && !this.initialVersionSent) { - this.buffalo.setPosition(EZSP_PARAMETERS_INDEX); - - this.buffalo.setCommandByte(EZSP_FRAME_ID_INDEX, lowByte(command)); - } else { - // convert to extended frame format - this.buffalo.setPosition(EZSP_EXTENDED_PARAMETERS_INDEX); - - this.buffalo.setCommandByte(EZSP_EXTENDED_FRAME_ID_LB_INDEX, lowByte(command)); - this.buffalo.setCommandByte(EZSP_EXTENDED_FRAME_ID_HB_INDEX, highByte(command)); - } - } - - /** - * Sends the current EZSP command frame. Returns EZSP_SUCCESS if the command was sent successfully. - * Any other return value means that an error has been detected by the serial protocol layer. - * - * if ezsp.sendCommand fails early, this will be: - * - EzspStatus.ERROR_INVALID_CALL - * - EzspStatus.NOT_CONNECTED - * - EzspStatus.ERROR_COMMAND_TOO_LONG - * - * if ezsp.sendCommand fails, this will be whatever ash.send returns: - * - EzspStatus.SUCCESS - * - EzspStatus.NO_TX_SPACE - * - EzspStatus.DATA_FRAME_TOO_SHORT - * - EzspStatus.DATA_FRAME_TOO_LONG - * - EzspStatus.NOT_CONNECTED - * - * if ezsp.sendCommand times out, this will be EzspStatus.ASH_ACK_TIMEOUT (XXX: for now) - * - * if ezsp.sendCommand resolves, this will be whatever ezsp.responseReceived returns: - * - EzspStatus.NO_RX_DATA (should not happen if command was sent (since we subscribe to frame event to trigger function)) - * - status from EzspFrameID.INVALID_COMMAND status byte - * - EzspStatus.ERROR_UNSUPPORTED_CONTROL - * - EzspStatus.ERROR_WRONG_DIRECTION - * - EzspStatus.ERROR_TRUNCATED - * - EzspStatus.SUCCESS - */ - private async sendCommand(): Promise { - if (!this.checkConnection()) { - debug("[SEND COMMAND] NOT CONNECTED"); - return EzspStatus.NOT_CONNECTED; - } - - this.buffalo.setCommandByte(EZSP_SEQUENCE_INDEX, this.nextFrameSequence()); - // we always set the network index in the ezsp frame control. - this.buffalo.setCommandByte( - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - (EZSP_FRAME_CONTROL_COMMAND | (DEFAULT_SLEEP_MODE & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((DEFAULT_NETWORK_INDEX << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - - // Send initial EZSP_VERSION command with old packet format for old Hosts/NCPs - if (!this.initialVersionSent && (this.buffalo.getCommandByte(EZSP_FRAME_ID_INDEX) === EzspFrameID.VERSION)) { - this.initialVersionSent = true; - } else { - this.buffalo.setCommandByte(EZSP_EXTENDED_FRAME_CONTROL_HB_INDEX, EZSP_EXTENDED_FRAME_FORMAT_VERSION); - } - - // might have tried to write more than allocated EZSP_MAX_FRAME_LENGTH for frameContents - // use write index to detect broken frames cases (inc'ed every time a byte is supposed to have been written) - // since index is always inc'ed on setCommandByte, this should always end at 202 max - const length: number = this.buffalo.getPosition(); - - if (length > EZSP_MAX_FRAME_LENGTH) { - // this.ezspErrorHandler(EzspStatus.ERROR_COMMAND_TOO_LONG);// XXX: this forces a NCP reset?? - return EzspStatus.ERROR_COMMAND_TOO_LONG; - } - - this.frameLength = length; - - let status: EzspStatus; - - debug(`===> ${this.frameToString}`); - - try { - 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])); - } - - 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); - }, this.ash.responseTimeout), - resolve, - }; - })); - - if (status !== EzspStatus.SUCCESS) { - throw status; - } - } catch (err) { - debug(`=x=> ${this.frameToString} Error: ${err}`); - - this.ezspErrorHandler(status); - } - - this.sendingCommand = false; - - return status; - } - - /** - * 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)) { - debug(`Skipping async callback while waiting for response to command.`); - - 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; - - debug(`<=== ${this.frameToString}`);// raw=${this.frameContents.subarray(0, this.frameLength).toString('hex')}`); - - 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); - - debug(`ERROR Host receive queue full. Dropping received callback: ${dropBuffer.data.toString('hex')}`); - - 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). - * @returns - */ - public responseReceived(): EzspStatus { - let status: EzspStatus; - - status = this.checkResponseReceived(); - - if (status === EzspStatus.NO_RX_DATA) { - return status; - } - - 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); - } - - if ((frameControl & EZSP_FRAME_CONTROL_DIRECTION_MASK) !== EZSP_FRAME_CONTROL_RESPONSE) { - status = EzspStatus.ERROR_WRONG_DIRECTION; - } - - if ((frameControl & EZSP_FRAME_CONTROL_TRUNCATED_MASK) === EZSP_FRAME_CONTROL_TRUNCATED) { - status = EzspStatus.ERROR_TRUNCATED; - } - - if ((frameControl & EZSP_FRAME_CONTROL_OVERFLOW_MASK) === EZSP_FRAME_CONTROL_OVERFLOW) { - status = EzspStatus.ERROR_OVERFLOW; - } - - 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) { - debug(`[RESPONSE RECEIVED] ERROR ${EzspStatus[status]}`); - this.ezspErrorHandler(status); - } - - this.buffalo.setPosition(parametersIndex); - - // An overflow does not indicate a comms failure; - // The system can still communicate but buffers are running critically low. - // This is almost always due to network congestion and goes away when the network becomes quieter. - if (status === EzspStatus.ERROR_OVERFLOW) { - return EzspStatus.SUCCESS; - } - - return status; - } - - /** - * Dispatches callback frames handlers. - */ - public callbackDispatch(): void { - switch (this.buffalo.getExtFrameId()) { - case EzspFrameID.NO_CALLBACKS: { - this.ezspNoCallbacks(); - break; - } - case EzspFrameID.STACK_TOKEN_CHANGED_HANDLER: { - const tokenAddress = this.buffalo.readUInt16(); - this.ezspStackTokenChangedHandler(tokenAddress); - break; - } - case EzspFrameID.TIMER_HANDLER: { - const timerId = this.buffalo.readUInt8(); - this.ezspTimerHandler(timerId); - break; - } - case EzspFrameID.COUNTER_ROLLOVER_HANDLER: { - const type: EmberCounterType = this.buffalo.readUInt8(); - this.ezspCounterRolloverHandler(type); - break; - } - case EzspFrameID.CUSTOM_FRAME_HANDLER: { - const payloadLength = this.buffalo.readUInt8(); - const payload = this.buffalo.readListUInt8({length: payloadLength}); - this.ezspCustomFrameHandler(payloadLength, payload); - break; - } - case EzspFrameID.STACK_STATUS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspStackStatusHandler(status); - break; - } - case EzspFrameID.ENERGY_SCAN_RESULT_HANDLER: { - const channel = this.buffalo.readUInt8(); - const maxRssiValue = this.buffalo.readUInt8(); - 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.readUInt8(); - this.ezspNetworkFoundHandler(networkFound, lastHopLqi, lastHopRssi); - break; - } - case EzspFrameID.SCAN_COMPLETE_HANDLER: { - const channel = this.buffalo.readUInt8(); - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspScanCompleteHandler(channel, status); - break; - } - case EzspFrameID.UNUSED_PAN_ID_FOUND_HANDLER: { - const panId: EmberPanId = 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 childType: EmberNodeType = this.buffalo.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(); - 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: EmberStatus = this.buffalo.readUInt8(); - this.ezspRemoteSetBindingHandler(entry, index, policyDecision); - break; - } - case EzspFrameID.REMOTE_DELETE_BINDING_HANDLER: { - const index = this.buffalo.readUInt8(); - const policyDecision: EmberStatus = this.buffalo.readUInt8(); - 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); - break; - } - case EzspFrameID.POLL_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspPollCompleteHandler(status); - break; - } - case EzspFrameID.POLL_HANDLER: { - const childId: EmberNodeId = this.buffalo.readUInt16(); - const transmitExpected: boolean = this.buffalo.readUInt8() === 1 ? true : false; - 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.readUInt8(); - 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 - ); - break; - } - case EzspFrameID.INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER: { - const source: EmberNodeId = this.buffalo.readUInt16(); - const longId: EmberEUI64 = 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(); - this.ezspIncomingRouteErrorHandler(status, target); - break; - } - case EzspFrameID.INCOMING_NETWORK_STATUS_HANDLER: { - const errorCode = this.buffalo.readUInt8(); - const target: EmberNodeId = 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 lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readUInt8(); - const relayCount = this.buffalo.readUInt8(); - const relayList = this.buffalo.readListUInt16({length: relayCount});//this.buffalo.readListUInt8({length: (relayCount * 2)}); - this.ezspIncomingRouteRecordHandler(source, sourceEui, lastHopLqi, lastHopRssi, relayCount, relayList); - break; - } - case EzspFrameID.ID_CONFLICT_HANDLER: { - const id: EmberNodeId = 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.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspMacPassthroughMessageHandler(messageType, lastHopLqi, lastHopRssi, 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.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspMacFilterMatchMessageHandler(filterIndexMatch, legacyPassthroughType, lastHopLqi, lastHopRssi, messageContents); - break; - } - case EzspFrameID.RAW_TRANSMIT_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspRawTransmitCompleteHandler(status); - break; - } - case EzspFrameID.SWITCH_NETWORK_KEY_HANDLER: { - const sequenceNumber = this.buffalo.readUInt8(); - this.ezspSwitchNetworkKeyHandler(sequenceNumber); - break; - } - case EzspFrameID.ZIGBEE_KEY_ESTABLISHMENT_HANDLER: { - const partner: EmberEUI64 = 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 status: EmberDeviceUpdate = this.buffalo.readUInt8(); - const policyDecision: EmberJoinDecision = this.buffalo.readUInt8(); - const parentOfNewNodeId: EmberNodeId = this.buffalo.readUInt16(); - this.ezspTrustCenterJoinHandler(newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId); - break; - } - case EzspFrameID.GENERATE_CBKE_KEYS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - const ephemeralPublicKey: EmberPublicKeyData = this.buffalo.readEmberPublicKeyData(); - this.ezspGenerateCbkeKeysHandler(status, ephemeralPublicKey); - break; - } - case EzspFrameID.CALCULATE_SMACS_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - 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 ephemeralPublicKey: EmberPublicKey283k1Data = this.buffalo.readEmberPublicKey283k1Data(); - this.ezspGenerateCbkeKeysHandler283k1(status, ephemeralPublicKey); - break; - } - case EzspFrameID.CALCULATE_SMACS_HANDLER283K1: { - const status: EmberStatus = this.buffalo.readUInt8(); - 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 messageContents = this.buffalo.readPayload(); - this.ezspDsaSignHandler(status, messageContents); - break; - } - case EzspFrameID.DSA_VERIFY_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - this.ezspDsaVerifyHandler(status); - break; - } - case EzspFrameID.MFGLIB_RX_HANDLER: { - const linkQuality = this.buffalo.readUInt8(); - const rssi = this.buffalo.readUInt8(); - const packetLength = this.buffalo.readUInt8(); - const packetContents = this.buffalo.readListUInt8({length: packetLength}); - this.ezspMfglibRxHandler(linkQuality, rssi, packetLength, packetContents); - break; - } - case EzspFrameID.INCOMING_BOOTLOAD_MESSAGE_HANDLER: { - const longId: EmberEUI64 = this.buffalo.readIeeeAddr(); - const lastHopLqi = this.buffalo.readUInt8(); - const lastHopRssi = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspIncomingBootloadMessageHandler(longId, lastHopLqi, lastHopRssi, messageContents); - break; - } - case EzspFrameID.BOOTLOAD_TRANSMIT_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - const messageContents = this.buffalo.readPayload(); - this.ezspBootloadTransmitCompleteHandler(status, 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.readUInt8(); - this.ezspZllNetworkFoundHandler(networkInfo, isDeviceInfoNull, deviceInfo, lastHopLqi, lastHopRssi); - break; - } - case EzspFrameID.ZLL_SCAN_COMPLETE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - 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.readUInt8(); - this.ezspZllAddressAssignmentHandler(addressInfo, lastHopLqi, lastHopRssi); - break; - } - case EzspFrameID.ZLL_TOUCH_LINK_TARGET_HANDLER: { - const networkInfo: EmberZllNetwork = this.buffalo.readEmberZllNetwork(); - this.ezspZllTouchLinkTargetHandler(networkInfo); - break; - } - case EzspFrameID.D_GP_SENT_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - const gpepHandle = this.buffalo.readUInt8(); - this.ezspDGpSentHandler(status, gpepHandle); - break; - } - case EzspFrameID.GPEP_INCOMING_MESSAGE_HANDLER: { - const status: EmberStatus = this.buffalo.readUInt8(); - const gpdLink = this.buffalo.readUInt8(); - const sequenceNumber = this.buffalo.readUInt8(); - const addr: EmberGpAddress = 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 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(); - this.ezspGpepIncomingMessageHandler( - status, - gpdLink, - sequenceNumber, - addr, - gpdfSecurityLevel, - gpdfSecurityKeyType, - autoCommissioning, - bidirectionalInfo, - gpdSecurityFrameCounter, - gpdCommandId, - mic, - proxyTableIndex, - gpdCommandPayload - ); - break; - } - default: - this.ezspErrorHandler(EzspStatus.ERROR_INVALID_FRAME_ID); - } - } - - /** - * - * @returns uint8_t - */ - private nextSendSequence(): number { - return (this.sendSequence = ((++this.sendSequence) & MESSAGE_TAG_MASK)); - } - - /** - * Calls ezspSend${x} based on type and takes care of tagging message. - * - * 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, - * 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. - * @param apsFrame [IN/OUT] EmberApsFrame * The APS frame which is to be added to the message. - * @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 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; - let apsSequence: number; - const messageTag = this.nextSendSequence(); - - switch (type) { - case EmberOutgoingMessageType.VIA_BINDING: - case EmberOutgoingMessageType.VIA_ADDRESS_TABLE: - case EmberOutgoingMessageType.DIRECT: { - [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_WITH_ALIAS: { - [status, apsSequence] = (await this.ezspSendMulticastWithAlias( - apsFrame, - apsFrame.radius/*radius*/, - apsFrame.radius/*nonmember radius*/, - alias, - sequence, - messageTag, - message - )); - break; - } - case EmberOutgoingMessageType.BROADCAST: { - [status, apsSequence] = (await this.ezspSendBroadcast( - indexOrDestination, - apsFrame, - ZA_MAX_HOPS/*radius*/, - messageTag, - message - )); - break; - } - case EmberOutgoingMessageType.BROADCAST_WITH_ALIAS: { - [status, apsSequence] = (await this.ezspProxyBroadcast( - alias, - indexOrDestination, - sequence, - apsFrame, - apsFrame.radius, - messageTag, - message - )); - break; - } - default: - break; - } - - apsFrame.sequence = apsSequence; - - // NOTE: match `~~~>` from adapter since this is just a wrapper for it - debug(`~~~> [SENT type=${EmberOutgoingMessageType[type]} apsSequence=${apsSequence} messageTag=${messageTag} status=${EmberStatus[status]}]`); - return [status, messageTag]; - } - - /** - * Retrieving the new version info. - * Wrapper for `ezspGetValue`. - * @returns Send status - * @returns EmberVersion*, null if status not SUCCESS. - */ - public async ezspGetVersionStruct(): Promise<[EzspStatus, version: EmberVersion]> { - const [status, outValueLength, outValue] = (await this.ezspGetValue(EzspValueId.VERSION_INFO, 7));// sizeof(EmberVersion) - - if (outValueLength !== 7) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, { - build : outValue[0] + ((outValue[1]) << 8), - major : outValue[2], - minor : outValue[3], - patch : outValue[4], - special: outValue[5], - type : outValue[6], - }]; - } - - /** - * Function for manipulating the endpoints flags on the NCP. - * Wrapper for `ezspGetExtendedValue` - * @param endpoint uint8_t - * @param flags EzspEndpointFlags - * @returns EzspStatus - */ - public async ezspSetEndpointFlags(endpoint: number, flags: EzspEndpointFlag): Promise { - return this.ezspSetValue(EzspValueId.ENDPOINT_FLAGS, 3, [endpoint, lowByte(flags), highByte(flags)]); - } - - /** - * Function for manipulating the endpoints flags on the NCP. - * Wrapper for `ezspGetExtendedValue`. - * @param endpoint uint8_t - * @returns EzspStatus - * @returns flags - */ - public async ezspGetEndpointFlags(endpoint: number): Promise<[EzspStatus, flags: EzspEndpointFlag]> { - const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.ENDPOINT_FLAGS, endpoint, 2)); - - if (outValLen < 2) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - const returnFlags = highLowToInt(outVal[1], outVal[0]); - - return [status, returnFlags]; - } - - /** - * Wrapper for `ezspGetExtendedValue`. - * @param EmberNodeId - * @param destination - * @returns EzspStatus - * @returns overhead uint8_t - */ - public async ezspGetSourceRouteOverhead(destination: EmberNodeId): Promise<[EzspStatus, overhead: number]> { - const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.GET_SOURCE_ROUTE_OVERHEAD, destination, 1)); - - if (outValLen < 1) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, outVal[0]]; - } - - /** - * Wrapper for `ezspGetExtendedValue`. - * @returns EzspStatus - * @returns reason - * @returns nodeId EmberNodeId* - */ - public async ezspGetLastLeaveReason(): Promise<[EzspStatus, reason: EmberLeaveReason, nodeId: EmberNodeId]> { - const [status, outValLen, outVal] = (await this.ezspGetExtendedValue(EzspExtendedValueId.LAST_LEAVE_REASON, 0, 3)); - - if (outValLen < 3) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, outVal[0], highLowToInt(outVal[2], outVal[1])]; - } - - /** - * Wrapper for `ezspGetValue`. - * @returns EzspStatus - * @returns reason - */ - public async ezspGetLastRejoinReason(): Promise<[EzspStatus, reason: EmberRejoinReason]> { - const [status, outValLen, outVal] = (await this.ezspGetValue(EzspValueId.LAST_REJOIN_REASON, 1)); - - if (outValLen < 1) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, outVal[0]]; - } - - /** - * Wrapper for `ezspSetValue`. - * @param mask - * @returns - */ - public async ezspSetExtendedSecurityBitmask(mask: EmberExtendedSecurityBitmask): Promise { - return this.ezspSetValue(EzspValueId.EXTENDED_SECURITY_BITMASK, 2, [lowByte(mask), highByte(mask)]); - } - - /** - * Wrapper for `ezspGetValue`. - * @returns - */ - public async ezspGetExtendedSecurityBitmask(): Promise<[EzspStatus, mask: EmberExtendedSecurityBitmask]> { - const [status, outValLen, outVal] = (await this.ezspGetValue(EzspValueId.EXTENDED_SECURITY_BITMASK, 2)); - - if (outValLen < 2) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, highLowToInt(outVal[1], outVal[0])]; - } - - /** - * Wrapper for `ezspSetValue`. - * @returns - */ - public async ezspStartWritingStackTokens(): Promise { - return this.ezspSetValue(EzspValueId.STACK_TOKEN_WRITING, 1, [1]); - } - - /** - * Wrapper for `ezspSetValue`. - * @returns - */ - public async ezspStopWritingStackTokens(): Promise { - return this.ezspSetValue(EzspValueId.STACK_TOKEN_WRITING, 1, [0]); - } - - //-----------------------------------------------------------------------------// - //---------------------------- START EZSP COMMANDS ----------------------------// - //-----------------------------------------------------------------------------// - - //----------------------------------------------------------------------------- - // Configuration Frames - //----------------------------------------------------------------------------- - - /** - * The command allows the Host to specify the desired EZSP version and must be - * sent before any other command. The response provides information about the - * firmware running on the NCP. - * - * @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. - */ - async ezspVersion(desiredProtocolVersion: number): Promise<[protocolVersion: number, stackType: number, stackVersion: number]> { - this.startCommand(EzspFrameID.VERSION); - this.buffalo.writeUInt8(desiredProtocolVersion); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const protocolVersion: number = this.buffalo.readUInt8(); - const stackType: number = this.buffalo.readUInt8(); - const stackVersion: number = this.buffalo.readUInt16(); - - return [protocolVersion, stackType, stackVersion]; - } - - /** - * Reads a configuration value from the NCP. - * - * @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. - */ - async ezspGetConfigurationValue(configId: EzspConfigId): Promise<[EzspStatus, value: number]> { - this.startCommand(EzspFrameID.GET_CONFIGURATION_VALUE); - this.buffalo.writeUInt8(configId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - const value: number = this.buffalo.readUInt16(); - - return [status, value]; - } - - /** - * Writes a configuration value to the NCP. Configuration values can be modified - * by the Host after the NCP has reset. Once the status of the stack changes to - * EMBER_NETWORK_UP, configuration values can no longer be modified and this - * command will respond with EzspStatus.ERROR_INVALID_CALL. - * - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Read attribute data on NCP endpoints. - * @param endpoint uint8_t Endpoint - * @param cluster uint16_t Cluster. - * @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. - */ - async ezspReadAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, readLength: number): - Promise<[EmberStatus, dataType: number, outReadLength: number, data: number[]]> { - this.startCommand(EzspFrameID.READ_ATTRIBUTE); - this.buffalo.writeUInt8(endpoint); - this.buffalo.writeUInt16(cluster); - this.buffalo.writeUInt16(attributeId); - this.buffalo.writeUInt8(mask); - this.buffalo.writeUInt16(manufacturerCode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const maxReadLength = readLength; - const status: EmberStatus = this.buffalo.readUInt8(); - const dataType = this.buffalo.readUInt8(); - readLength = this.buffalo.readUInt8(); - - if (readLength > maxReadLength) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - const data = this.buffalo.readListUInt8({length: readLength}); - - return [status, dataType, readLength, data]; - } - - /** - * Write attribute data on NCP endpoints. - * @param endpoint uint8_t Endpoint - * @param cluster uint16_t Cluster. - * @param attributeId uint16_t Attribute ID. - * @param mask uint8_t Mask. - * @param manufacturerCode uint16_t Manufacturer code. - * @param overrideReadOnlyAndDataType Override read only and data type. - * @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. - */ - async ezspWriteAttribute(endpoint: number, cluster: number, attributeId: number, mask: number, manufacturerCode: number, - overrideReadOnlyAndDataType: boolean, justTest: boolean, dataType: number, data: Buffer): Promise { - this.startCommand(EzspFrameID.WRITE_ATTRIBUTE); - this.buffalo.writeUInt8(endpoint); - this.buffalo.writeUInt16(cluster); - this.buffalo.writeUInt16(attributeId); - this.buffalo.writeUInt8(mask); - this.buffalo.writeUInt16(manufacturerCode); - this.buffalo.writeUInt8(overrideReadOnlyAndDataType ? 1 : 0); - this.buffalo.writeUInt8(justTest ? 1 : 0); - this.buffalo.writeUInt8(dataType); - this.buffalo.writePayload(data); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Configures endpoint information on the NCP. The NCP does not remember these - * settings after a reset. Endpoints can be added by the Host after the NCP has - * reset. Once the status of the stack changes to EMBER_NETWORK_UP, endpoints - * can no longer be added and this command will respond with EzspStatus.ERROR_INVALID_CALL. - * @param endpoint uint8_t The application endpoint to be added. - * @param profileId uint16_t The endpoint's application profile. - * @param deviceId uint16_t The endpoint's device ID within the application profile. - * @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. - */ - async ezspAddEndpoint(endpoint: number, profileId: number, deviceId: number, deviceVersion: number, - inputClusterList: number[], outputClusterList: number[]): Promise { - this.startCommand(EzspFrameID.ADD_ENDPOINT); - this.buffalo.writeUInt8(endpoint); - this.buffalo.writeUInt16(profileId); - this.buffalo.writeUInt16(deviceId); - this.buffalo.writeUInt8(deviceVersion); - this.buffalo.writeUInt8(inputClusterList.length); - this.buffalo.writeUInt8(outputClusterList.length); - this.buffalo.writeListUInt16(inputClusterList); - this.buffalo.writeListUInt16(outputClusterList); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Allows the Host to change the policies used by the NCP to make fast - * decisions. - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * 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. - */ - async ezspGetPolicy(policyId: EzspPolicyId): Promise<[EzspStatus, number]> { - this.startCommand(EzspFrameID.GET_POLICY); - this.buffalo.writeUInt8(policyId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - const decisionId: number = this.buffalo.readUInt8(); - - return [status, decisionId]; - } - - /** - * Triggers a pan id update message. - * @param The new Pan Id - * @returns true if the request was successfully handed to the stack, false otherwise - */ - async ezspSendPanIdUpdate(newPan: EmberPanId): Promise { - this.startCommand(EzspFrameID.SEND_PAN_ID_UPDATE); - this.buffalo.writeUInt16(newPan); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: boolean = this.buffalo.readUInt8() === 1 ? true : false; - - return status; - } - - /** - * 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. - * 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. - */ - async ezspGetValue(valueId: EzspValueId, valueLength: number): - Promise<[EzspStatus, outValueLength: number, outValue: number[]]> { - this.startCommand(EzspFrameID.GET_VALUE); - this.buffalo.writeUInt8(valueId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - // let value: number[] = null; - - const maxValueLength = valueLength; - const status: EzspStatus = this.buffalo.readUInt8(); - valueLength = this.buffalo.readUInt8(); - - if (valueLength > maxValueLength) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - const value = this.buffalo.readListUInt8({length: valueLength}); - - return [status, valueLength, value]; - } - - /** - * Reads a value from the NCP but passes an extra argument specific to the value - * being retrieved. - * @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. - * 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. - */ - async ezspGetExtendedValue(valueId: EzspExtendedValueId, characteristics: number, valueLength: number): - Promise<[EzspStatus, outValueLength: number, outValue: number[]]> { - this.startCommand(EzspFrameID.GET_EXTENDED_VALUE); - this.buffalo.writeUInt8(valueId); - this.buffalo.writeUInt32(characteristics); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - // let value: number[] = null; - - const maxValueLength = valueLength; - const status: EzspStatus = this.buffalo.readUInt8(); - valueLength = this.buffalo.readUInt8(); - - if (valueLength > maxValueLength) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - const value = this.buffalo.readListUInt8({length: valueLength}); - - return [status, valueLength, value]; - } - - /** - * Writes a value to the NCP. - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EzspStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - //----------------------------------------------------------------------------- - // Utilities Frames - //----------------------------------------------------------------------------- - /** - * A command which does nothing. The Host can use this to set the sleep mode or to check the status of the NCP. - */ - async ezspNop(): Promise { - this.startCommand(EzspFrameID.NOP); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * 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. - */ - async ezspEcho(data: Buffer): Promise { - this.startCommand(EzspFrameID.ECHO); - this.buffalo.writePayload(data); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const echo = this.buffalo.readPayload(); - - if (echo.length > data.length) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return echo; - } - - /** - * Allows the NCP to respond with a pending callback. - */ - async ezspCallback(): Promise { - this.startCommand(EzspFrameID.CALLBACK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - this.callbackDispatch(); - } - - /** - * Callback - * Indicates that there are currently no pending callbacks. - */ - ezspNoCallbacks(): void { - debug(`ezspNoCallbacks(): callback called`); - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * 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. - */ - async ezspGetToken(tokenId: number): Promise<[EmberStatus, tokenData: number[]]> { - this.startCommand(EzspFrameID.GET_TOKEN); - this.buffalo.writeUInt8(tokenId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const tokenData = this.buffalo.readListUInt8({length: 8}); - - return [status, tokenData]; - } - - /** - * 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. - */ - async ezspGetMfgToken(tokenId: EzspMfgTokenId): Promise<[number, tokenData: number[]]> { - this.startCommand(EzspFrameID.GET_MFG_TOKEN); - this.buffalo.writeUInt8(tokenId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const tokenDataLength = this.buffalo.readUInt8(); - let expectedTokenDataLength: number = 0; - - // the size of corresponding the EZSP Mfg token, please refer to app/util/ezsp/ezsp-enum.h - switch (tokenId) { - // 2 bytes - case EzspMfgTokenId.CUSTOM_VERSION: - case EzspMfgTokenId.MANUF_ID: - case EzspMfgTokenId.PHY_CONFIG: - case EzspMfgTokenId.CTUNE: - expectedTokenDataLength = 2; - break; - // 8 bytes - case EzspMfgTokenId.EZSP_STORAGE: - case EzspMfgTokenId.CUSTOM_EUI_64: - expectedTokenDataLength = 8; - break; - // 16 bytes - case EzspMfgTokenId.STRING: - case EzspMfgTokenId.BOARD_NAME: - case EzspMfgTokenId.BOOTLOAD_AES_KEY: - expectedTokenDataLength = 16; - break; - // 20 bytes - case EzspMfgTokenId.INSTALLATION_CODE: - expectedTokenDataLength = 20; - break; - // 40 bytes - case EzspMfgTokenId.ASH_CONFIG: - expectedTokenDataLength = 40; - break; - // 92 bytes - case EzspMfgTokenId.CBKE_DATA: - expectedTokenDataLength = 92; - break; - default: - break; - } - - if (tokenDataLength != expectedTokenDataLength) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - const tokenData = this.buffalo.readListUInt8({length: tokenDataLength}); - - return [tokenDataLength, tokenData]; - } - - /** - * Sets a manufacturing token in the Customer Information Block (CIB) area of - * the NCP if that token currently unset (fully erased). Cannot be used with - * EZSP_STACK_CAL_DATA, EZSP_STACK_CAL_FILTER, EZSP_MFG_ASH_CONFIG, or - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Callback - * A callback invoked to inform the application that a stack token has changed. - * @param tokenAddress uint16_t The address of the stack token that has changed. - */ - ezspStackTokenChangedHandler(tokenAddress: number): void { - debug(`ezspStackTokenChangedHandler(): callback called with: [tokenAddress=${tokenAddress}]`); - } - - /** - * Returns a pseudorandom number. - * @returns - * - Always returns EMBER_SUCCESS. - * - uint16_t * A pseudorandom number. - */ - async ezspGetRandomNumber(): Promise<[EmberStatus, value: number]> { - this.startCommand(EzspFrameID.GET_RANDOM_NUMBER); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const value = this.buffalo.readUInt16(); - - return [status, value]; - } - - /** - * Sets a timer on the NCP. There are 2 independent timers available for use by the Host. - * A timer can be cancelled by setting time to 0 or units to EMBER_EVENT_INACTIVE. - * @param timerId uint8_t Which timer to set (0 or 1). - * @param time uint16_t The delay before the timerHandler callback will be generated. - * Note that the timer clock is free running and is not synchronized with this command. - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Gets information about a timer. The Host can use this command to find out how - * 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. - */ - async ezspGetTimer(timerId: number): Promise<[number, units: EmberEventUnits, repeat: boolean]> { - this.startCommand(EzspFrameID.GET_TIMER); - this.buffalo.writeUInt8(timerId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const time = this.buffalo.readUInt16(); - const units = this.buffalo.readUInt8(); - const repeat = this.buffalo.readUInt8() === 1 ? true : false; - - return [time, units, repeat]; - } - - /** - * Callback - * A callback from the timer. - * @param timerId uint8_t Which timer generated the callback (0 or 1). - */ - ezspTimerHandler(timerId: number): void { - debug(`ezspTimerHandler(): callback called with: [timerId=${timerId}]`); - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Retrieves and clears Ember counters. See the EmberCounterType enumeration for the counter types. - * @returns uint16_t * A list of all counter values ordered according to the EmberCounterType enumeration. - */ - async ezspReadAndClearCounters(): Promise { - this.startCommand(EzspFrameID.READ_AND_CLEAR_COUNTERS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const values = this.buffalo.readListUInt16({length: EmberCounterType.COUNT}); - - return values; - } - - /** - * Retrieves Ember counters. See the EmberCounterType enumeration for the counter types. - * @returns uint16_t * A list of all counter values ordered according to the EmberCounterType enumeration. - */ - async ezspReadCounters(): Promise { - this.startCommand(EzspFrameID.READ_COUNTERS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const values = this.buffalo.readListUInt16({length: EmberCounterType.COUNT}); - - return values; - } - - /** - * Callback - * This call is fired when a counter exceeds its threshold - * @param type Type of Counter - */ - ezspCounterRolloverHandler(type: EmberCounterType): void { - debug(`ezspCounterRolloverHandler(): callback called with: [type=${EmberCounterType[type]}]`); - } - - /** - * Used to test that UART flow control is working correctly. - * @param delay uint16_t Data will not be read from the host for this many milliseconds. - */ - async ezspDelayTest(delay: number): Promise { - this.startCommand(EzspFrameID.DELAY_TEST); - this.buffalo.writeUInt16(delay); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * This retrieves the status of the passed library ID to determine if it is compiled into the stack. - * @param libraryId The ID of the library being queried. - * @returns The status of the library being queried. - */ - async ezspGetLibraryStatus(libraryId: EmberLibraryId): Promise { - this.startCommand(EzspFrameID.GET_LIBRARY_STATUS); - this.buffalo.writeUInt8(libraryId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberLibraryStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Allows the HOST to know whether the NCP is running the XNCP library. If so, - * 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. - */ - async ezspGetXncpInfo(): Promise<[EmberStatus, manufacturerId: number, versionNumber: number]> { - this.startCommand(EzspFrameID.GET_XNCP_INFO); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const manufacturerId = this.buffalo.readUInt16(); - const versionNumber = this.buffalo.readUInt16(); - - return [status, manufacturerId, versionNumber]; - } - - /** - * Provides the customer a custom EZSP frame. On the NCP, these frames are only - * handled if the XNCP library is included. On the NCP side these frames are - * handled in the emberXNcpIncomingCustomEzspMessageCallback() callback - * 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. - */ - async ezspCustomFrame(payload: Buffer, replyLength: number): - Promise<[EmberStatus, outReply: Buffer]> { - this.startCommand(EzspFrameID.CUSTOM_FRAME); - this.buffalo.writePayload(payload); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const reply = this.buffalo.readPayload(); - - if (reply.length > replyLength) { - throw EzspStatus.ERROR_INVALID_VALUE; - } - - return [status, reply]; - } - - /** - * 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 { - debug(`ezspCustomFrameHandler(): callback called with: [payloadLength=${payloadLength}], [payload=${payload}]`); - } - - /** - * Returns the EUI64 ID of the local node. - * @returns The 64-bit ID. - */ - async ezspGetEui64(): Promise { - this.startCommand(EzspFrameID.GET_EUI64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const eui64 = this.buffalo.readIeeeAddr(); - - return eui64; - } - - /** - * Returns the 16-bit node ID of the local node. - * @returns The 16-bit ID. - */ - async ezspGetNodeId(): Promise { - this.startCommand(EzspFrameID.GET_NODE_ID); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const nodeId: EmberNodeId = this.buffalo.readUInt16(); - - return nodeId; - } - - /** - * Returns number of phy interfaces present. - * @returns uint8_t Value indicate how many phy interfaces present. - */ - async ezspGetPhyInterfaceCount(): Promise { - this.startCommand(EzspFrameID.GET_PHY_INTERFACE_COUNT); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const interfaceCount = this.buffalo.readUInt8(); - - return interfaceCount; - } - - /** - * Returns the entropy source used for true random number generation. - * @returns Value indicates the used entropy source. - */ - async ezspGetTrueRandomEntropySource(): Promise { - this.startCommand(EzspFrameID.GET_TRUE_RANDOM_ENTROPY_SOURCE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const entropySource: EmberEntropySource = this.buffalo.readUInt8(); - - return entropySource; - } - - //----------------------------------------------------------------------------- - // Networking Frames - //----------------------------------------------------------------------------- - - /** - * Sets the manufacturer code to the specified value. - * The manufacturer code is one of the fields of the node descriptor. - * @param code uint16_t The manufacturer code for the local node. - */ - async ezspSetManufacturerCode(code: number): Promise { - this.startCommand(EzspFrameID.SET_MANUFACTURER_CODE); - this.buffalo.writeUInt16(code); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Sets the power descriptor to the specified value. The power descriptor is a - * dynamic value. Therefore, you should call this function whenever the value - * changes. - * @param descriptor uint16_t The new power descriptor for the local node. - */ - async ezspSetPowerDescriptor(descriptor: number): Promise { - this.startCommand(EzspFrameID.SET_POWER_DESCRIPTOR); - this.buffalo.writeUInt16(descriptor); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Resume network operation after a reboot. The node retains its original type. - * This should be called on startup whether or not the node was previously part - * 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. - */ - async ezspNetworkInit(networkInitStruct: EmberNetworkInitStruct): Promise { - this.startCommand(EzspFrameID.NETWORK_INIT); - this.buffalo.writeEmberNetworkInitStruct(networkInitStruct); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Returns a value indicating whether the node is joining, joined to, or leaving a network. - * @returns Command send status. - * @returns An EmberNetworkStatus value indicating the current join status. - */ - async ezspNetworkState(): Promise { - this.startCommand(EzspFrameID.NETWORK_STATE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberNetworkStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Callback - * A callback invoked when the status of the stack changes. If the status - * parameter equals EMBER_NETWORK_UP, then the getNetworkParameters command can - * be called to obtain the new network parameters. If any of the parameters are - * being stored in nonvolatile memory by the Host, the stored values should be - * updated. - * @param status Stack status - */ - ezspStackStatusHandler(status: EmberStatus): void { - debug(`ezspStackStatusHandler(): callback called with: [status=${EmberStatus[status]}]`); - - this.emit(EzspEvents.STACK_STATUS, status); - } - - /** - * This function will start a scan. - * @param scanType Indicates the type of scan to be performed. Possible values are: EZSP_ENERGY_SCAN and EZSP_ACTIVE_SCAN. - * For each type, the respective callback for reporting results is: energyScanResultHandler and networkFoundHandler. - * The energy scan and active scan report errors and completion via the scanCompleteHandler. - * @param channelMask uint32_t Bits set as 1 indicate that this particular channel should be scanned. - * Bits set to 0 indicate that this particular channel should not be scanned. For example, a channelMask value of 0x00000001 - * would indicate that only channel 0 should be scanned. Valid channels range from 11 to 26 inclusive. - * This translates to a channel mask value of 0x07FFF800. - * As a convenience, a value of 0 is reinterpreted as the mask for the current channel. - * @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. - */ - async ezspStartScan(scanType: EzspNetworkScanType, channelMask: number, duration: number): Promise { - this.startCommand(EzspFrameID.START_SCAN); - this.buffalo.writeUInt8(scanType); - this.buffalo.writeUInt32(channelMask); - this.buffalo.writeUInt8(duration); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = this.buffalo.readUInt32(); - - return status; - } - - /** - * Callback - * Reports the result of an energy scan for a single channel. The scan is not - * complete until the scanCompleteHandler callback is called. - * @param channel uint8_t The 802.15.4 channel number that was scanned. - * @param maxRssiValue int8_t The maximum RSSI value found on the channel. - */ - ezspEnergyScanResultHandler(channel: number, maxRssiValue: number): void { - debug(`ezspEnergyScanResultHandler(): callback called with: [channel=${channel}], [maxRssiValue=${maxRssiValue}]`); - console.log(`Energy scan for channel ${channel} reports max RSSI value at ${maxRssiValue}.`); - } - - /** - * Callback - * Reports that a network was found as a result of a prior call to startScan. - * Gives the network parameters useful for deciding which network to join. - * @param networkFound EmberZigbeeNetwork * The parameters associated with the network found. - * @param lastHopLqi uint8_t The link quality from the node that generated this beacon. - * @param lastHopRssi int8_t The energy level (in units of dBm) observed during the reception. - */ - ezspNetworkFoundHandler(networkFound: EmberZigbeeNetwork, lastHopLqi: number, lastHopRssi: number): void { - debug(`ezspNetworkFoundHandler(): callback called with: [networkFound=${networkFound}], ` - + `[lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}]`); - } - - /** - * 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. - */ - ezspScanCompleteHandler(channel: number, status: EmberStatus): void { - debug(`ezspScanCompleteHandler(): callback called with: [channel=${channel}], [status=${EmberStatus[status]}]`); - } - - /** - * Callback - * This function returns an unused panID and channel pair found via the find - * unused panId scan procedure. - * @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 { - debug(`ezspUnusedPanIdFoundHandler(): callback called with: [panId=${panId}], [channel=${channel}]`); - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Terminates a scan in progress. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspStopScan(): Promise { - this.startCommand(EzspFrameID.STOP_SCAN); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * 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. - */ - async ezspFormNetwork(parameters: EmberNetworkParameters): Promise { - this.startCommand(EzspFrameID.FORM_NETWORK); - this.buffalo.writeEmberNetworkParameters(parameters); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Causes the stack to associate with the network using the specified network - * parameters. It can take several seconds for the stack to associate with the - * local network. Do not send messages until the stackStatusHandler callback - * informs you that the stack is up. - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - 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 - * to associate with the local network. Do not send messages until the - * stackStatusHandler callback informs you that the stack is up. Unlike - * ::emberJoinNetwork(), this function does not issue an active scan before - * joining. Instead, it will cause the local node to issue a MAC Association - * Request directly to the specified target node. It is assumed that the beacon - * parameter is an artifact after issuing an active scan. (For more information, - * see emberGetBestBeacon and emberGetNextBeacon.) - * @param localNodeType Specifies 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 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. - */ - async ezspJoinNetworkDirectly(localNodeType: EmberNodeType, beacon: EmberBeaconData, radioTxPower: number, clearBeaconsAfterNetworkUp: boolean) - : Promise { - this.startCommand(EzspFrameID.JOIN_NETWORK_DIRECTLY); - this.buffalo.writeUInt8(localNodeType); - this.buffalo.writeEmberBeaconData(beacon); - this.buffalo.writeUInt8(radioTxPower); - this.buffalo.writeUInt8(clearBeaconsAfterNetworkUp ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - 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. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspLeaveNetwork(): Promise { - this.startCommand(EzspFrameID.LEAVE_NETWORK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - 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 - * communicate with its parent and wishes to find a new one. Another case is - * when a device has missed a Network Key update and no longer has the current - * Network Key. The stack will call ezspStackStatusHandler to indicate that the - * network is down, then try to re-establish contact with the network by - * performing an active scan, choosing a network with matching extended pan id, - * and sending a ZigBee network rejoin request. A second call to the - * ezspStackStatusHandler callback indicates either the success or the failure - * of the attempt. The process takes approximately 150 milliseconds per channel - * to complete. - * @param haveCurrentNetworkKey This parameter tells the stack whether to try to use the current network key. - * If it has the current network key it will perform a secure rejoin (encrypted). If this fails the device should try an unsecure rejoin. - * 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 { - this.startCommand(EzspFrameID.FIND_AND_REJOIN_NETWORK); - this.buffalo.writeUInt8(haveCurrentNetworkKey ? 1 : 0); - this.buffalo.writeUInt32(channelMask); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Tells the stack to allow other nodes to join the network with this node as - * 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. - */ - async ezspPermitJoining(duration: number): Promise { - this.startCommand(EzspFrameID.PERMIT_JOINING); - this.buffalo.writeUInt8(duration); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Callback - * Indicates that a child has joined or left. - * @param index uint8_t The index of the child of interest. - * @param joining True if the child is joining. False the child is leaving. - * @param childId The node ID of the child. - * @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 { - debug(`ezspChildJoinHandler(): callback called with: [index=${index}], [joining=${joining}], ` - + `[childId=${childId}], [childEui64=${childEui64}], [childType=${childType}]`); - } - - /** - * Sends a ZDO energy scan request. This request may only be sent by the current - * network manager and must be unicast, not broadcast. See ezsp-utils.h for - * related macros emberSetNetworkManagerRequest() and - * emberChangeChannelRequest(). - * @param target The network address of the node to perform the scan. - * @param scanChannels uint32_t A mask of the channels to be scanned - * @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. - */ - async ezspEnergyScanRequest(target: EmberNodeId, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Returns the current network parameters. - * @returns An EmberStatus value indicating success or the reason for failure. - * @returns EmberNodeType * An EmberNodeType value indicating the current node type. - * @returns EmberNetworkParameters * The current network parameters. - */ - async ezspGetNetworkParameters(): Promise<[EmberStatus, nodeType: EmberNodeType, parameters: EmberNetworkParameters]> { - this.startCommand(EzspFrameID.GET_NETWORK_PARAMETERS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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]> { - this.startCommand(EzspFrameID.GET_RADIO_PARAMETERS); - this.buffalo.writeUInt8(phyIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const parameters = this.buffalo.readEmberMultiPhyRadioParameters(); - - return [status, parameters]; - } - - /** - * Returns information about the children of the local node and the parent of - * 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 - * (coordinators and nodes that are not joined to a network). - */ - async ezspGetParentChildParameters(): Promise<[number, parentEui64: EmberEUI64, parentNodeId: EmberNodeId]> { - this.startCommand(EzspFrameID.GET_PARENT_CHILD_PARAMETERS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const childCount = this.buffalo.readUInt8(); - const parentEui64 = this.buffalo.readIeeeAddr(); - const parentNodeId = this.buffalo.readUInt16(); - - return [childCount, parentEui64, parentNodeId]; - } - - /** - * 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]> { - this.startCommand(EzspFrameID.GET_CHILD_DATA); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const childData = this.buffalo.readEmberChildData(); - - return [status, childData]; - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - 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 { - this.startCommand(EzspFrameID.CHILD_ID); - this.buffalo.writeUInt8(childIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const childId: EmberNodeId = this.buffalo.readUInt16(); - - return childId; - } - - /** - * 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 { - this.startCommand(EzspFrameID.CHILD_INDEX); - this.buffalo.writeUInt16(childId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const childIndex = this.buffalo.readUInt8(); - - return childIndex; - } - - /** - * Returns the source route table total size. - * @returns uint8_t Total size of source route table. - */ - async ezspGetSourceRouteTableTotalSize(): Promise { - this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_TOTAL_SIZE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const sourceRouteTableTotalSize = this.buffalo.readUInt8(); - - return sourceRouteTableTotalSize; - } - - /** - * Returns the number of filled entries in source route table. - * @returns uint8_t The number of filled entries in source route table. - */ - async ezspGetSourceRouteTableFilledSize(): Promise { - this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_FILLED_SIZE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const sourceRouteTableFilledSize = this.buffalo.readUInt8(); - - return sourceRouteTableFilledSize; - } - - /** - * 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 EmberNodeId * 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]> { - this.startCommand(EzspFrameID.GET_SOURCE_ROUTE_TABLE_ENTRY); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const destination = this.buffalo.readUInt16(); - const closerIndex = this.buffalo.readUInt8(); - - return [status, destination, closerIndex]; - } - - /** - * Returns the neighbor table entry at the given index. The number of active - * 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 EmberNeighborTableEntry * The contents of the neighbor table entry. - */ - async ezspGetNeighbor(index: number): Promise<[EmberStatus, value: EmberNeighborTableEntry]> { - this.startCommand(EzspFrameID.GET_NEIGHBOR); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const value = this.buffalo.readEmberNeighborTableEntry(); - - return [status, value]; - } - - /** - * Return EmberStatus depending on whether the frame counter of the node is - * found in the neighbor or child table. This function gets the last received - * 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 uint32_t * Return the frame counter of the node from the neighbor or child table - */ - async ezspGetNeighborFrameCounter(eui64: EmberEUI64): Promise<[EmberStatus, returnFrameCounter: number]> { - this.startCommand(EzspFrameID.GET_NEIGHBOR_FRAME_COUNTER); - this.buffalo.writeIeeeAddr(eui64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const returnFrameCounter = this.buffalo.readUInt32(); - - return [status, returnFrameCounter]; - } - - /** - * Sets the frame counter for the neighbour or child. - * @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 - */ - async ezspSetNeighborFrameCounter(eui64: EmberEUI64, frameCounter: number): Promise { - this.startCommand(EzspFrameID.SET_NEIGHBOR_FRAME_COUNTER); - this.buffalo.writeIeeeAddr(eui64); - this.buffalo.writeUInt32(frameCounter); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * 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. - */ - async ezspSetRoutingShortcutThreshold(costThresh: number): Promise { - this.startCommand(EzspFrameID.SET_ROUTING_SHORTCUT_THRESHOLD); - this.buffalo.writeUInt8(costThresh); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - - return status; - } - - /** - * Gets the routing shortcut threshold used to differentiate between directly - * using a neighbor vs. performing routing. - * @returns uint8_t The routing shortcut threshold - */ - async ezspGetRoutingShortcutThreshold(): Promise { - this.startCommand(EzspFrameID.GET_ROUTING_SHORTCUT_THRESHOLD); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const routingShortcutThresh = this.buffalo.readUInt8(); - return routingShortcutThresh; - } - - /** - * Returns the number of active entries in the neighbor table. - * @returns uint8_t The number of active entries in the neighbor table. - */ - async ezspNeighborCount(): Promise { - this.startCommand(EzspFrameID.NEIGHBOR_COUNT); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const value = this.buffalo.readUInt8(); - return value; - } - - /** - * Returns the route table entry at the given index. The route table size can be - * 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. - * @returns EmberRouteTableEntry * The contents of the route table entry. - */ - async ezspGetRouteTableEntry(index: number): Promise<[EmberStatus, value: EmberRouteTableEntry]> { - this.startCommand(EzspFrameID.GET_ROUTE_TABLE_ENTRY); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const value = this.buffalo.readEmberRouteTableEntry(); - - return [status, value]; - } - - /** - * Sets the radio output power at which a node is operating. Ember radios have - * discrete power settings. For a list of available power settings, see the - * technical specification for the RF communication module in your Developer - * Kit. Note: Care should be taken when using this API on a running network, as - * it will directly impact the established link qualities neighboring nodes have - * 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. - */ - async ezspSetRadioPower(power: number): Promise { - this.startCommand(EzspFrameID.SET_RADIO_POWER); - this.buffalo.writeUInt8(power); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sets the channel to use for sending and receiving messages. For a list of - * available radio channels, see the technical specification for the RF - * 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. - */ - async ezspSetRadioChannel(channel: number): Promise { - this.startCommand(EzspFrameID.SET_RADIO_CHANNEL); - this.buffalo.writeUInt8(channel); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Gets the channel in use for sending and receiving messages. - * @returns uint8_t Current radio channel. - */ - async ezspGetRadioChannel(): Promise { - this.startCommand(EzspFrameID.GET_RADIO_CHANNEL); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const channel = this.buffalo.readUInt8(); - - return channel; - } - - /** - * 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. - */ - async ezspSetRadioIeee802154CcaMode(ccaMode: number): Promise { - this.startCommand(EzspFrameID.SET_RADIO_IEEE802154_CCA_MODE); - this.buffalo.writeUInt8(ccaMode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Enable/disable concentrator support. - * @param on If this bool is true the concentrator support is enabled. Otherwise is disabled. - * If this bool is false all the other arguments are ignored. - * @param concentratorType uint16_t Must be either EMBER_HIGH_RAM_CONCENTRATOR or EMBER_LOW_RAM_CONCENTRATOR. - * The former is used when the caller has enough memory to store source routes for the whole network. - * In that case, remote nodes stop sending route records once the concentrator has successfully received one. - * 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 minTime uint16_t The minimum amount of time that must pass between MTORR broadcasts. - * @param maxTime uint16_t The maximum amount of time that can pass between MTORR broadcasts. - * @param routeErrorThreshold uint8_t The number of route errors that will trigger a re-broadcast of the MTORR. - * @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. - */ - async ezspSetConcentrator(on: boolean, concentratorType: number, minTime: number, maxTime: number, routeErrorThreshold: number, - deliveryFailureThreshold: number, maxHops: number): Promise { - this.startCommand(EzspFrameID.SET_CONCENTRATOR); - this.buffalo.writeUInt8(on ? 1 : 0); - this.buffalo.writeUInt16(concentratorType); - this.buffalo.writeUInt16(minTime); - this.buffalo.writeUInt16(maxTime); - this.buffalo.writeUInt8(routeErrorThreshold); - this.buffalo.writeUInt8(deliveryFailureThreshold); - this.buffalo.writeUInt8(maxHops); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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 { - this.startCommand(EzspFrameID.SET_BROKEN_ROUTE_ERROR_CODE); - this.buffalo.writeUInt8(errorCode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This causes to initialize the desired radio interface other than native and - * form a new network by becoming the coordinator with same panId as native - * radio network. - * @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. - * @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. - */ - 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); - this.buffalo.writeUInt8(channel); - this.buffalo.writeUInt8(power); - this.buffalo.writeUInt8(bitmask); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async ezspMultiPhyStop(phyIndex: number): Promise { - this.startCommand(EzspFrameID.MULTI_PHY_STOP); - this.buffalo.writeUInt8(phyIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sets the radio output power for desired phy interface at which a node is - * operating. Ember radios have discrete power settings. For a list of available - * power settings, see the technical specification for the RF communication - * module in your Developer Kit. Note: Care should be taken when using this api - * on a running network, as it will directly impact the established link - * qualities neighboring nodes have with the node on which it is called. This - * 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. - */ - async ezspMultiPhySetRadioPower(phyIndex: number, power: number): Promise { - this.startCommand(EzspFrameID.MULTI_PHY_SET_RADIO_POWER); - this.buffalo.writeUInt8(phyIndex); - this.buffalo.writeUInt8(power); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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 { - this.startCommand(EzspFrameID.SEND_LINK_POWER_DELTA_REQUEST); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sets the channel for desired phy interface to use for sending and receiving - * messages. For a list of available radio pages and channels, see the technical - * specification for the RF 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 page and channel. - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Obtains the current duty cycle state. - * @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]> { - this.startCommand(EzspFrameID.GET_DUTY_CYCLE_STATE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const returnedState = this.buffalo.readUInt8(); - - return [status, returnedState]; - } - - /** - * 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 - */ - async ezspSetDutyCycleLimitsInStack(limits: EmberDutyCycleLimits): Promise { - this.startCommand(EzspFrameID.SET_DUTY_CYCLE_LIMITS_IN_STACK); - this.buffalo.writeEmberDutyCycleLimits(limits); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - * @returns EmberDutyCycleLimits * Return current duty cycle limits if returnedLimits is not NULL - */ - async ezspGetDutyCycleLimits(): Promise<[EmberStatus, returnedLimits: EmberDutyCycleLimits]> { - this.startCommand(EzspFrameID.GET_DUTY_CYCLE_LIMITS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const returnedLimits = this.buffalo.readEmberDutyCycleLimits(); - - return [status, returnedLimits]; - } - - /** - * Returns the duty cycle of the stack's connected children that are being - * monitored, up to maxDevices. It indicates the amount of overall duty cycle - * they have consumed (up to the suspend limit). The first entry is always the - * local stack's nodeId, and thus the total aggregate duty cycle for the device. - * 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. - * @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. - */ - async ezspGetCurrentDutyCycle(maxDevices: number): Promise<[EmberStatus, arrayOfDeviceDutyCycles: number[]]> { - this.startCommand(EzspFrameID.GET_CURRENT_DUTY_CYCLE); - this.buffalo.writeUInt8(maxDevices); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const arrayOfDeviceDutyCycles = this.buffalo.readListUInt8({length: 134}); - - return [status, arrayOfDeviceDutyCycles]; - } - - /** - * Callback - * Callback fires when the duty cycle state has changed - * @param channelPage uint8_t The channel page whose duty cycle state has changed. - * @param channel uint8_t The channel number whose duty cycle state has changed. - * @param state The current duty cycle state. - * @param totalDevices uint8_t The total number of connected end devices that are being monitored for duty cycle. - * @param arrayOfDeviceDutyCycles EmberPerDeviceDutyCycle * Consumed duty cycles of end devices that are being monitored. - * The first entry always be the local stack's nodeId, and thus the total aggregate duty cycle for the device. - */ - ezspDutyCycleHandler(channelPage: number, channel: number, state: EmberDutyCycleState, totalDevices: number, - arrayOfDeviceDutyCycles: EmberPerDeviceDutyCycle[]): void { - debug(`ezspDutyCycleHandler(): callback called with: [channelPage=${channelPage}], [channel=${channel}], ` - + `[state=${state}], [totalDevices=${totalDevices}], [arrayOfDeviceDutyCycles=${arrayOfDeviceDutyCycles}]`); - } - - /** - * Returns the first beacon in the cache. Beacons are stored in cache after - * issuing an active scan. - * @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. - */ - async ezspGetFirstBeacon(): Promise<[EmberStatus, beaconIterator: EmberBeaconIterator]> { - this.startCommand(EzspFrameID.GET_FIRST_BEACON); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const beaconIterator = this.buffalo.readEmberBeaconIterator(); - - return [status, beaconIterator]; - } - - /** - * 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. - */ - async ezspGetNextBeacon(): Promise<[EmberStatus, beacon: EmberBeaconData]> { - this.startCommand(EzspFrameID.GET_NEXT_BEACON); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const numBeacons = this.buffalo.readUInt8(); - - return numBeacons; - } - - /** - * Clears all cached beacons that have been collected from a scan. - */ - async ezspClearStoredBeacons(): Promise { - this.startCommand(EzspFrameID.CLEAR_STORED_BEACONS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * 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. - */ - async ezspSetLogicalAndRadioChannel(radioChannel: number): Promise { - this.startCommand(EzspFrameID.SET_LOGICAL_AND_RADIO_CHANNEL); - this.buffalo.writeUInt8(radioChannel); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - //----------------------------------------------------------------------------- - // Binding Frames - //----------------------------------------------------------------------------- - - /** - * Deletes all binding table entries. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspClearBindingTable(): Promise { - this.startCommand(EzspFrameID.CLEAR_BINDING_TABLE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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 EmberBindingTableEntry * The contents of the binding entry. - */ - async ezspGetBinding(index: number): Promise<[EmberStatus, value: EmberBindingTableEntry]> { - this.startCommand(EzspFrameID.GET_BINDING); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const value = this.buffalo.readEmberBindingTableEntry(); - - return [status, value]; - } - - /** - * 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. - */ - async ezspDeleteBinding(index: number): Promise { - this.startCommand(EzspFrameID.DELETE_BINDING); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Indicates whether any messages are currently being sent using this binding - * table entry. Note that this command does not indicate whether a binding is - * clear. To determine whether a binding is clear, check whether the type field - * of the EmberBindingTableEntry has the value EMBER_UNUSED_BINDING. - * @param index uint8_t The index of a binding table entry. - * @returns True if the binding table entry is active, false otherwise. - */ - async ezspBindingIsActive(index: number): Promise { - this.startCommand(EzspFrameID.BINDING_IS_ACTIVE); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const active = this.buffalo.readUInt8() === 1 ? true : false; - - return active; - } - - /** - * Returns the node ID for the binding's destination, if the ID is known. If a - * message is sent using the binding and the destination's ID is not known, the - * stack will discover the ID by broadcasting a ZDO address request. The - * application can avoid the need for this discovery by using - * setBindingRemoteNodeId when it knows the correct ID via some other means. The - * destination's node ID is forgotten when the binding is changed, when the - * local node reboots or, much more rarely, when the destination node changes - * its ID in response to an ID conflict. - * @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 { - this.startCommand(EzspFrameID.GET_BINDING_REMOTE_NODE_ID); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const nodeId: EmberNodeId = this.buffalo.readUInt16(); - - return nodeId; - } - - /** - * Set the node ID for the binding's destination. See getBindingRemoteNodeId for - * a description. - * @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 { - this.startCommand(EzspFrameID.SET_BINDING_REMOTE_NODE_ID); - this.buffalo.writeUInt8(index); - this.buffalo.writeUInt16(nodeId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Callback - * The NCP used the external binding modification policy to decide how to handle - * a remote set binding request. The Host cannot change the current decision, - * but it can change the policy for future decisions using the setPolicy - * 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. - */ - ezspRemoteSetBindingHandler(entry: EmberBindingTableEntry, index: number, policyDecision: EmberStatus): void { - debug(`ezspRemoteSetBindingHandler(): callback called with: [entry=${entry}], [index=${index}], ` - + `[policyDecision=${EmberStatus[policyDecision]}]`); - } - - /** - * Callback - * The NCP used the external binding modification policy to decide how to handle - * a remote delete binding request. The Host cannot change the current decision, - * 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. - */ - ezspRemoteDeleteBindingHandler(index: number, policyDecision: EmberStatus): void { - debug(`ezspRemoteDeleteBindingHandler(): callback called with: [index=${index}], [policyDecision=${EmberStatus[policyDecision]}]`); - } - - //----------------------------------------------------------------------------- - // Messaging Frames - //----------------------------------------------------------------------------- - - /** - * Returns the maximum size of the payload. The size depends on the security level in use. - * @returns uint8_t The maximum APS payload length. - */ - async ezspMaximumPayloadLength(): Promise { - this.startCommand(EzspFrameID.MAXIMUM_PAYLOAD_LENGTH); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const apsLength = this.buffalo.readUInt8(); - - return apsLength; - } - - /** - * 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. - * @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, - * 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 messageContents uint8_t * Content of the message. - * @returns An EmberStatus 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]> { - this.startCommand(EzspFrameID.SEND_UNICAST); - this.buffalo.writeUInt8(type); - this.buffalo.writeUInt16(indexOrDestination); - this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(messageTag); - this.buffalo.writePayload(messageContents); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const apsSequence = this.buffalo.readUInt8(); - - return [status, apsSequence]; - } - - /** - * Sends a broadcast message as per the ZigBee specification. - * @param destination The destination to which to send the broadcast. This must be one of the three ZigBee broadcast addresses. - * @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 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 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]> { - this.startCommand(EzspFrameID.SEND_BROADCAST); - this.buffalo.writeUInt16(destination); - this.buffalo.writeEmberApsFrame(apsFrame); - this.buffalo.writeUInt8(radius); - this.buffalo.writeUInt8(messageTag); - this.buffalo.writePayload(messageContents); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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. - */ - 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); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - 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 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 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]> { - 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]); - } - - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const apsSequence = this.buffalo.readUInt8(); - return [status, apsSequence]; - } - - /** - * Sends a reply to a received unicast message. The incomingMessageHandler - * callback for the unicast being replied to supplies the values for all the - * parameters except the reply itself. - * @param sender Value supplied by incoming unicast. - * @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. - * - EMBER_INVALID_CALL - The EZSP_UNICAST_REPLIES_POLICY is set to 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. - * 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. - */ - async ezspSendReply(sender: EmberNodeId, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback indicating the stack has completed sending a message. - * @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 { - debug(`ezspMessageSentHandler(): callback called with: [type=${EmberOutgoingMessageType[type]}], [indexOrDestination=${indexOrDestination}], ` - + `[apsFrame=${JSON.stringify(apsFrame)}], [messageTag=${messageTag}], [status=${EmberStatus[status]}], ` - + `[messageContents=${messageContents.toString('hex')}]`); - - if (status === EmberStatus.DELIVERY_FAILED) { - // no ACK was received from the destination - this.emit(EzspEvents.MESSAGE_SENT_DELIVERY_FAILED, type, indexOrDestination, apsFrame, messageTag); - } - // shouldn't be any other status except SUCCESS... no use for it atm - } - - /** - * Sends a route request packet that creates routes from every node in the - * network back to this node. This function should be called by an application - * that wishes to communicate with many nodes, for example, a gateway, central - * monitor, or controller. A device using this function was referred to as an - * 'aggregator' in EmberZNet 2.x and earlier, and is referred to as a - * 'concentrator' in the ZigBee specification and EmberZNet 3. This function - * enables large scale networks, because the other devices do not have to - * individually perform bandwidth-intensive route discoveries. Instead, when a - * remote node sends an APS unicast to a concentrator, its network layer - * automatically delivers a special route record packet first, which lists the - * network ids of all the intermediate relays. The concentrator can then use - * source routing to send outbound APS unicasts. (A source routed message is one - * in which the entire route is listed in the network layer header.) This allows - * the concentrator to communicate with thousands of devices without requiring - * large route tables on neighboring nodes. This function is only available in - * ZigBee Pro (stack profile 2), and cannot be called on end devices. Any router - * can be a concentrator (not just the coordinator), and there can be multiple - * concentrators on a network. Note that a concentrator does not automatically - * obtain routes to all network nodes after calling this function. Remote - * applications must first initiate an inbound APS unicast. Many-to-one routes - * are not repaired automatically. Instead, the concentrator application must - * call this function to rediscover the routes as necessary, for example, upon - * failure of a retried APS message. The reason for this is that there is no - * scalable one-size-fits-all route repair strategy. A common and recommended - * strategy is for the concentrator application to refresh the routes by calling - * this function periodically. - * @param concentratorType uint16_t Must be either EMBER_HIGH_RAM_CONCENTRATOR or EMBER_LOW_RAM_CONCENTRATOR. - * The former is used when the caller has enough memory to store source routes for the whole network. - * In that case, remote nodes stop sending route records once the concentrator has successfully received one. - * 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. - */ - 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: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - 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: EmberStatus): void { - debug(`ezspPollCompleteHandler(): callback called with: [status=${EmberStatus[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: EmberNodeId, transmitExpected: boolean): void { - debug(`ezspPollHandler(): callback called with: [childId=${childId}], [transmitExpected=${transmitExpected}]`); - } - - /** - * 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 { - debug(`ezspIncomingSenderEui64Handler(): callback called with: [senderEui64=${senderEui64}]`); - } - - /** - * Callback - * A callback indicating a message has been received. - * @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 messageContents uint8_t * The incoming message. - */ - ezspIncomingMessageHandler(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, lastHopRssi: number, - sender: EmberNodeId, bindingIndex: number, addressIndex: number, messageContents: Buffer): void { - 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')}]`); - // from protocol\zigbee\app\util\zigbee-framework\zigbee-device-host.h - 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) { - debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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({length: assocDevCount}); - } - - debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE status=${status} eui64=${eui64} nodeId=${nodeId} startIndex=${startIndex} ` - + `assocDevList=${assocDevList}]`); - debug(`<=== [ZDO IEEE_ADDRESS_RESPONSE] Support not implemented upstream`); - - const payload: IEEEAddressResponsePayload = {eui64, nodeId, assocDevList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case NETWORK_ADDRESS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO NETWORK_ADDRESS_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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({length: assocDevCount}); - } - - debug(`<=== [ZDO NETWORK_ADDRESS_RESPONSE status=${status} eui64=${eui64} nodeId=${nodeId} startIndex=${startIndex} ` - + `assocDevList=${assocDevList}]`); - - const payload: NetworkAddressResponsePayload = {eui64, nodeId, assocDevList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case MATCH_DESCRIPTORS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - const endpointCount = zdoBuffalo.readUInt8(); - const endpointList = zdoBuffalo.readListUInt8({length: endpointCount}); - - debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE status=${status} nodeId=${nodeId} endpointList=${endpointList}]`); - debug(`<=== [ZDO MATCH_DESCRIPTORS_RESPONSE] Support not implemented upstream`); - - const payload: MatchDescriptorsResponsePayload = {nodeId, endpointList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case SIMPLE_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO SIMPLE_DESCRIPTOR_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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({length: inClusterCount}); - const outClusterCount = zdoBuffalo.readUInt8(); - const outClusterList = zdoBuffalo.readListUInt16({length: outClusterCount}); - - debug(`<=== [ZDO SIMPLE_DESCRIPTOR_RESPONSE status=${status} nodeId=${nodeId} endpoint=${endpoint} profileId=${profileId} ` - + `deviceId=${deviceId} deviceVersion=${deviceVersion} inClusterList=${inClusterList} outClusterList=${outClusterList}]`); - - const payload: SimpleDescriptorResponsePayload = { - nodeId, - endpoint, - profileId, - deviceId, - inClusterList, - outClusterList, - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case NODE_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO NODE_DESCRIPTOR_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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(); - - debug(`<=== [ZDO NODE_DESCRIPTOR_RESPONSE status=${status} nodeId=${nodeId} logicalType=${logicalType} ` - + `freqBand=${freqBand} macCapFlags=${byteToBits(macCapFlags)} manufacturerCode=${manufacturerCode} maxBufSize=${maxBufSize} ` - + `maxIncTxSize=${maxIncTxSize} stackRevision=${stackRevision} maxOutTxSize=${maxOutTxSize}]`); - - const payload: NodeDescriptorResponsePayload = { - nodeId, - logicalType, - macCapFlags: getMacCapFlags(macCapFlags), - manufacturerCode, - stackRevision - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case POWER_DESCRIPTOR_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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()); - - debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE status=${status} nodeId=${nodeId} currentPowerMode=${currentPowerMode} ` - + `availPowerSources=${availPowerSources} currentPowerSource=${currentPowerSource} ` - + `currentPowerSourceLevel=${currentPowerSourceLevel}]`); - debug(`<=== [ZDO POWER_DESCRIPTOR_RESPONSE] Support not implemented upstream`); - - const payload: PowerDescriptorResponsePayload = { - nodeId, - currentPowerMode, - availPowerSources, - currentPowerSource, - currentPowerSourceLevel - }; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case ACTIVE_ENDPOINTS_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO ACTIVE_ENDPOINTS_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, null); - } else { - const nodeId = zdoBuffalo.readUInt16(); - const endpointCount = zdoBuffalo.readUInt8(); - const endpointList = zdoBuffalo.readListUInt8({length: endpointCount}); - - debug(`<=== [ZDO ACTIVE_ENDPOINTS_RESPONSE status=${status} nodeId=${nodeId} endpointList=${endpointList}]`); - - const payload: ActiveEndpointsResponsePayload = {nodeId, endpointList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case LQI_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO LQI_TABLE_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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({length: 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, - }); - } - - debug(`<=== [ZDO LQI_TABLE_RESPONSE status=${status} neighborTableEntries=${neighborTableEntries} startIndex=${startIndex} ` - + `entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`); - - const payload: LQITableResponsePayload = {neighborTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case ROUTING_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO ROUTING_TABLE_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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, - }); - } - - debug(`<=== [ZDO ROUTING_TABLE_RESPONSE status=${status} routingTableEntries=${routingTableEntries} startIndex=${startIndex} ` - + `entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`); - - const payload: RoutingTableResponsePayload = {routingTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case BINDING_TABLE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - if (status !== EmberZdoStatus.ZDP_SUCCESS) { - debug(`<=== [ZDO BINDING_TABLE_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, 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, - }); - } - - debug(`<=== [ZDO BINDING_TABLE_RESPONSE status=${status} bindingTableEntries=${bindingTableEntries} startIndex=${startIndex} ` - + `entryCount=${entryCount} entryList=${JSON.stringify(entryList)}]`); - debug(`<=== [ZDO BINDING_TABLE_RESPONSE] Support not implemented upstream`); - - const payload: BindingTableResponsePayload = {bindingTableEntries, entryList}; - - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame, payload); - } - break; - } - case BIND_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - debug(`<=== [ZDO BIND_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); - break; - } - case UNBIND_RESPONSE:{ - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - debug(`<=== [ZDO UNBIND_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); - break; - } - case LEAVE_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - debug(`<=== [ZDO LEAVE_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); - break; - } - case PERMIT_JOINING_RESPONSE: { - const status: EmberZdoStatus = zdoBuffalo.readUInt8(); - - debug(`<=== [ZDO PERMIT_JOINING_RESPONSE status=${status}]`); - this.emit(EzspEvents.ZDO_RESPONSE, status, sender, apsFrame); - break; - } - case END_DEVICE_ANNOUNCE: { - const nodeId = zdoBuffalo.readUInt16(); - const eui64 = zdoBuffalo.readIeeeAddr(); - /** @see MACCapabilityFlags */ - const capabilities = zdoBuffalo.readUInt8(); - - debug(`<=== [ZDO END_DEVICE_ANNOUNCE nodeId=${nodeId} eui64=${eui64} capabilities=${byteToBits(capabilities)}]`); - - 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); - break; - } - default: { - console.log(`<=== [ZDO clusterId=${apsFrame.clusterId}] Support not implemented`); - break; - } - } - } else if (apsFrame.profileId === HA_PROFILE_ID || apsFrame.profileId === WILDCARD_PROFILE_ID) { - this.emit(EzspEvents.INCOMING_MESSAGE, type, apsFrame, lastHopLqi, sender, messageContents); - } else if (apsFrame.profileId === 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 - * @returns uint32_t Remaining time(ms) until next MTORR broadcast if the mode is on, MAX_INT32U_VALUE if the mode is off - */ - async ezspSetSourceRouteDiscoveryMode(mode: EmberSourceRouteDiscoveryMode): Promise { - this.startCommand(EzspFrameID.SET_SOURCE_ROUTE_DISCOVERY_MODE); - this.buffalo.writeUInt8(mode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const remainingTime = this.buffalo.readUInt32(); - - return remainingTime; - } - - /** - * 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 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 { - debug(`ezspIncomingManyToOneRouteRequestHandler(): callback called with: [source=${source}], [longId=${longId}], [cost=${cost}]`); - } - - /** - * Callback - * A callback invoked when a route error message is received. - * The error indicates that a problem routing to or from the target node was encountered. - * - * A status of ::EMBER_SOURCE_ROUTE_FAILURE indicates that a source-routed unicast sent from this node encountered a broken link. - * Note that this case occurs only if this node is a concentrator using many-to-one routing for inbound messages and source-routing for - * outbound messages. The node prior to the broken link generated the route error message and returned it to us along the many-to-one route. - * - * A status of ::EMBER_MANY_TO_ONE_ROUTE_FAILURE also occurs only if the local device is a concentrator, and indicates that a unicast sent - * to the local device along a many-to-one route encountered a broken link. The node prior to the broken link generated the route error - * message and forwarded it to the local device via a randomly chosen neighbor, taking advantage of the many-to-one nature of the route. - * - * A status of ::EMBER_MAC_INDIRECT_TIMEOUT indicates that a message sent to the target end device could not be delivered by the parent - * because the indirect transaction timer expired. Upon receipt of the route error, the stack sets the extended timeout for the target node - * in the address table, if present. It then calls this handler to indicate receipt of the error. - * - * Note that if the original unicast data message is sent using the ::EMBER_APS_OPTION_RETRY option, a new route error message is generated - * for each failed retry. Therefore, it is not unusual to receive three route error messages in succession for a single failed retried APS - * unicast. On the other hand, it is also not guaranteed that any route error messages will be delivered successfully at all. - * The only sure way to detect a route failure is to use retried APS messages and to check the status of the ::emberMessageSentHandler(). - * - * @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 { - debug(`ezspIncomingRouteErrorHandler(): callback called with: [status=${EmberStatus[status]}], [target=${target}]`); - // NOTE: This can trigger immediately after removal of a device with status MAC_INDIRECT_TIMEOUT - } - - /** - * Callback - * A callback invoked when a network status/route error message is received. - * The error indicates that there was a problem sending/receiving messages from the target node. - * - * Note: Network analyzer may flag this message as "route error" which is the old name for the "network status" command. - * - * This handler is a superset of ezspIncomingRouteErrorHandler. The old API was only invoking the handler for a couple of the possible - * error codes and these were being translated into EmberStatus. - * - * @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 { - debug(`ezspIncomingNetworkStatusHandler(): callback called with: [errorCode=${EmberStackError[errorCode]}], [target=${target}]`); - console.log(`Received network/route error ${EmberStackError[errorCode]} for "${target}".`); - } - - /** - * 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 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, - lastHopRssi: number, relayCount: number, relayList: number[]): void { - debug(`ezspIncomingRouteRecordHandler(): callback called with: [source=${source}], [sourceEui=${sourceEui}], ` - + `[lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}], [relayCount=${relayCount}], [relayList=${relayList}]`); - // 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 EMBER_SUCCESS if the source route was successfully stored, and - * EMBER_NO_BUFFERS otherwise. - */ - async ezspSetSourceRoute(destination: EmberNodeId, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Send the network key to a destination. - * @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 - */ - async ezspUnicastCurrentNetworkKey(targetShort: EmberNodeId, targetLong: EmberEUI64, parentShortId: EmberNodeId): 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Indicates whether any messages are currently being sent using this address - * table entry. Note that this function does not indicate whether the address - * table entry is unused. To determine whether an address table entry is unused, - * check the remote node ID. The remote node ID will have the value - * EMBER_TABLE_ENTRY_UNUSED_NODE_ID when the address table entry is not in use. - * @param uint8_tThe index of an address table entry. - * @returns True if the address table entry is active, false otherwise. - */ - async ezspAddressTableEntryIsActive(addressTableIndex: number): Promise { - this.startCommand(EzspFrameID.ADDRESS_TABLE_ENTRY_IS_ACTIVE); - this.buffalo.writeUInt8(addressTableIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const active = this.buffalo.readUInt8() === 1 ? true : false; - - 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. - */ - 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]); - } - - 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.buffalo.writeUInt8(addressTableIndex); - this.buffalo.writeUInt16(id); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Gets the EUI64 of an address table entry. - * @param addressTableIndex uint8_t The index of an address table entry. - * @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); - this.buffalo.writeUInt8(addressTableIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - 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; - } - - /** - * Tells the stack whether or not the normal interval between retransmissions of a retried unicast message should - * be increased by EMBER_INDIRECT_TRANSMISSION_TIMEOUT. - * The interval needs to be increased when sending to a sleepy node so that the message is not retransmitted until the destination - * has had time to wake up and poll its parent. - * The stack will automatically extend the timeout: - * - For our own sleepy children. - * - When an address response is received from a parent on behalf of its child. - * - When an indirect transaction expiry route error is received. - * - When an end device announcement is received from a sleepy node. - * @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. - */ - async ezspSetExtendedTimeout(remoteEui64: EmberEUI64, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Indicates whether or not the stack will extend the normal interval between - * 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. - */ - async ezspGetExtendedTimeout(remoteEui64: EmberEUI64): Promise { - this.startCommand(EzspFrameID.GET_EXTENDED_TIMEOUT); - this.buffalo.writeIeeeAddr(remoteEui64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const extendedTimeout = this.buffalo.readUInt8() === 1 ? true : false; - - return extendedTimeout; - } - - /** - * Replaces the EUI64, short ID and extended timeout setting of an address table - * entry. The previous EUI64, short ID and extended timeout setting are - * returned. - * @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. - * 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 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. - * 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]> { - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const oldEui64 = this.buffalo.readIeeeAddr(); - const oldId = this.buffalo.readUInt16(); - const oldExtendedTimeout = this.buffalo.readUInt8() === 1 ? true : false; - - return [status, oldEui64, oldId, oldExtendedTimeout]; - } - - /** - * 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. - */ - async ezspLookupNodeIdByEui64(eui64: EmberEUI64): Promise { - this.startCommand(EzspFrameID.LOOKUP_NODE_ID_BY_EUI64); - this.buffalo.writeIeeeAddr(eui64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const nodeId: EmberNodeId = this.buffalo.readUInt16(); - - return nodeId; - } - - /** - * 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 eui64 The EUI64 of the node. - */ - async ezspLookupEui64ByNodeId(nodeId: EmberNodeId): Promise<[EmberStatus, eui64: EmberEUI64]> { - this.startCommand(EzspFrameID.LOOKUP_EUI64_BY_NODE_ID); - this.buffalo.writeUInt16(nodeId); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const eui64 = this.buffalo.readIeeeAddr(); - - return [status, eui64]; - } - - /** - * 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 EmberMulticastTableEntry * The contents of the multicast entry. - */ - async ezspGetMulticastTableEntry(index: number): Promise<[EmberStatus, value: EmberMulticastTableEntry]> { - this.startCommand(EzspFrameID.GET_MULTICAST_TABLE_ENTRY); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const value = this.buffalo.readEmberMulticastTableEntry(); - - return [status, value]; - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback invoked by the EmberZNet stack when an id conflict is discovered, - * that is, two different nodes in the network were found to be using the same - * short id. The stack automatically removes the conflicting short id from its - * internal tables (address, binding, route, neighbor, and child tables). The - * application should discontinue any other use of the id. - * @param id The short id for which a conflict was detected - */ - ezspIdConflictHandler(id: EmberNodeId): void { - debug(`ezspIdConflictHandler(): callback called with: [id=${id}]`); - console.warn(`An ID conflict was detected for device "${id}".`); - } - - /** - * 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. - */ - async ezspWriteNodeData(erase: boolean): Promise { - this.startCommand(EzspFrameID.WRITE_NODE_DATA); - this.buffalo.writeUInt8(erase ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[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. - * @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. - */ - async ezspSendRawMessageExtended(messageContents: Buffer, priority: number, useCca: boolean): Promise { - this.startCommand(EzspFrameID.SEND_RAW_MESSAGE_EXTENDED); - this.buffalo.writePayload(messageContents); - this.buffalo.writeUInt8(priority); - this.buffalo.writeUInt8(useCca ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * 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 messageContents uint8_t * The raw message that was received. - */ - ezspMacPassthroughMessageHandler(messageType: EmberMacPassthroughType, lastHopLqi: number, lastHopRssi: number, messageContents: Buffer): void { - debug(`ezspMacPassthroughMessageHandler(): callback called with: [messageType=${messageType}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}], [messageContents=${messageContents.toString('hex')}]`); - } - - /** - * Callback - * A callback invoked by the EmberZNet stack when a raw MAC message that has - * 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 messageContents uint8_t * The raw message that was received. - */ - ezspMacFilterMatchMessageHandler(filterIndexMatch: number, legacyPassthroughType: EmberMacPassthroughType, lastHopLqi: number, - lastHopRssi: number, messageContents: Buffer): void { - debug(`ezspMacFilterMatchMessageHandler(): callback called with: [filterIndexMatch=${filterIndexMatch}], ` - + `[legacyPassthroughType=${legacyPassthroughType}], [lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}], ` - + `[messageContents=${messageContents.toString('hex')}]`); - - // TODO: needs triple-checking, this is only valid for InterPAN messages - const msgBuffalo = new EzspBuffalo(messageContents, 0); - - const macFrameControl = msgBuffalo.readUInt16() & ~(MAC_ACK_REQUIRED); - // 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; - - if (macFrameControl === LONG_DEST_FRAME_CONTROL) { - destAddress = msgBuffalo.readIeeeAddr(); - } else if (macFrameControl === SHORT_DEST_FRAME_CONTROL) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - destAddress = msgBuffalo.readUInt16(); - } else { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN macFrameControl "${macFrameControl}".`); - return; - } - - const sourcePanId: EmberPanId = msgBuffalo.readUInt16(); - const sourceAddress: EmberEUI64 = msgBuffalo.readIeeeAddr(); - - // Now that we know the correct MAC length, verify the interpan frame is the correct length. - let remainingLength = msgBuffalo.getBufferLength() - msgBuffalo.getPosition(); - - if (remainingLength < (STUB_NWK_SIZE + MIN_STUB_APS_SIZE)) { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length "${remainingLength}".`); - return; - } - - const nwkFrameControl = msgBuffalo.readUInt16(); - remainingLength -= 2;// read 2 more bytes before APS stuff - - if (nwkFrameControl !== STUB_NWK_FRAME_CONTROL) { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN nwkFrameControl "${nwkFrameControl}".`); - return; - } - - const apsFrameControl = msgBuffalo.readUInt8(); - - if ((apsFrameControl & ~(INTERPAN_APS_FRAME_DELIVERY_MODE_MASK) & ~(INTERPAN_APS_FRAME_SECURITY)) - !== INTERPAN_APS_FRAME_CONTROL_NO_DELIVERY_MODE) { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN apsFrameControl "${apsFrameControl}".`); - return; - } - - const messageType = (apsFrameControl & INTERPAN_APS_FRAME_DELIVERY_MODE_MASK); - let groupId: number = null; - - switch (messageType) { - case EmberInterpanMessageType.UNICAST: - case EmberInterpanMessageType.BROADCAST: { - if (remainingLength < INTERPAN_APS_UNICAST_BROADCAST_SIZE) { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length "${remainingLength}".`); - return; - } - break; - } - case EmberInterpanMessageType.MULTICAST: { - if (remainingLength < INTERPAN_APS_MULTICAST_SIZE) { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN length "${remainingLength}".`); - return; - } - - groupId = msgBuffalo.readUInt16(); - break; - } - default: { - debug(`ezspMacFilterMatchMessageHandler INVALID InterPAN messageType "${messageType}".`); - return; - } - } - - const clusterId = msgBuffalo.readUInt16(); - const profileId = msgBuffalo.readUInt16(); - const payload = msgBuffalo.readRest(); - - if (profileId === TOUCHLINK_PROFILE_ID && clusterId === Cluster.touchlink.ID) { - this.emit(EzspEvents.TOUCHLINK_MESSAGE, sourcePanId, sourceAddress, groupId, lastHopLqi, payload); - } - } - - /** - * Callback - * A callback invoked by the EmberZNet stack when the MAC has finished - * transmitting a raw message. - * @param status EMBER_SUCCESS if the transmission was successful, or EMBER_DELIVERY_FAILED if not - */ - ezspRawTransmitCompleteHandler(status: EmberStatus): void { - debug(`ezspRawTransmitCompleteHandler(): callback called with: [status=${EmberStatus[status]}]`); - } - - /** - * 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. - */ - async ezspSetMacPollFailureWaitTime(waitBeforeRetryIntervalMs: number): Promise { - this.startCommand(EzspFrameID.SET_MAC_POLL_FAILURE_WAIT_TIME); - this.buffalo.writeUInt32(waitBeforeRetryIntervalMs); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * 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 - */ - async ezspSetBeaconClassificationParams(param: EmberBeaconClassificationParams): Promise { - this.startCommand(EzspFrameID.SET_BEACON_CLASSIFICATION_PARAMS); - this.buffalo.writeEmberBeaconClassificationParams(param); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Gets the priority masks and related variables for choosing the best beacon. - * @returns The attempt to get the pramaters returns EMBER_SUCCESS - * @returns EmberBeaconClassificationParams * Gets the beacon prioritization related variable - */ - async ezspGetBeaconClassificationParams(): Promise<[EmberStatus, param: EmberBeaconClassificationParams]> { - this.startCommand(EzspFrameID.GET_BEACON_CLASSIFICATION_PARAMS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const param = this.buffalo.readEmberBeaconClassificationParams(); - - return [status, param]; - } - - //----------------------------------------------------------------------------- - // Security Frames - //----------------------------------------------------------------------------- - - /** - * Sets the security state that will be used by the device when it forms or - * joins the network. This call should not be used when restoring saved network - * state via networkInit as this will result in a loss of security data and will - * cause communication problems when the device re-enters the network. - * @param state EmberInitialSecurityState * The security configuration to be set. - * @returns The success or failure code of the operation. - */ - async ezspSetInitialSecurityState(state: EmberInitialSecurityState): Promise { - this.startCommand(EzspFrameID.SET_INITIAL_SECURITY_STATE); - this.buffalo.writeEmberInitialSecurityState(state); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Gets the current security state that is being used by a device that is joined - * in the network. - * @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]> { - this.startCommand(EzspFrameID.GET_CURRENT_SECURITY_STATE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const state = this.buffalo.readEmberCurrentSecurityState(); - - return [status, state]; - } - - /** - * 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. - */ - async ezspExportKey(context: SecManContext): Promise<[key: SecManKey, status: SLStatus]> { - /** - * Export a key from storage. Certain keys are indexed, while others are not, as described here. - * - * If context->core_key_type is.. - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, then context->key_index dictates whether to - * export the current (active) network key (index 0) or the alternate network - * key (index 1). - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK, then context->eui64 is checked if - * context->flags is set to ZB_SEC_MAN_FLAG_EUI_IS_VALID. If the EUI supplied - * does not match the TC EUI stored on the local device (if it is known), then - * an error is thrown. - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK_WITH_TIMEOUT, then keys may be searched by - * context->eui64 or context->key_index. context->flags determines how to search - * (see ::sl_zigbee_sec_man_flags_t). - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK, then keys may be searched by - * context->eui64 or context->key_index. context->flags determines how to search - * (see ::sl_zigbee_sec_man_flags_t). - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_PROXY_TABLE_KEY or - * SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_SINK_TABLE_KEY, then context->key_index - * dictates which key entry to export. These Green Power keys are indexed keys, - * and there are EMBER_GP_PROXY_TABLE_SIZE/EMBER_GP_SINK_TABLE_SIZE many of them. - * - * For all other key types, both context->key_index and context->eui64 are not used. - * - * @param context sl_zb_sec_man_context_t* [IN/OUT] The context to set. The context dictates which key - * type to export, which key_index (if applicable) into the relevant key - * storage, which eui64 (if applicable), etc. - * @param plaintext_key sl_zb_sec_man_key_t* [OUT] The key to export. - * - * @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. - */ - // NOTE: added for good measure - if (context.coreKeyType === SecManKeyType.INTERNAL) { - console.assert(false, `ezspImportKey cannot use INTERNAL key type.`); - return [null, SLStatus.INVALID_PARAMETER]; - } - - this.startCommand(EzspFrameID.EXPORT_KEY); - this.buffalo.writeSecManContext(context); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const key = this.buffalo.readSecManKey(); - const status: SLStatus = this.buffalo.readUInt32(); - - return [key, status]; - } - - /** - * Imports a key into security manager based on passed context. - * @param context sl_zb_sec_man_context_t * Metadata to identify where the imported key should be stored. - * @param key sl_zb_sec_man_key_t * The key to be imported. - * @returns The success or failure code of the operation. - */ - async ezspImportKey(context: SecManContext, key: SecManKey): Promise { - /** - * Import a key into storage. Certain keys are - * indexed, while others are not, as described here. - * - * If context->core_key_type is.. - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, then context->key_index dictates whether to - * import the current (active) network key (index 0) or the alternate network - * key (index 1). - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK_WITH_TIMEOUT, then context->eui64 must be - * set. context->key_index is unused. - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK, then context->key_index determines which - * index in the persisted key table that the entry should be stored to. - * context->eui64 must also be set. - * If context->key_index is 0xFF, a suitable key index will be found (either one - * storing an existing key with address of context->eui64, or an open entry), - * and context->key_index will be updated with where the entry was stored. - * - * ..SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_PROXY_TABLE_KEY or - * SL_ZB_SEC_MAN_KEY_TYPE_GREEN_POWER_SINK_TABLE_KEY, then context->key_index - * dictates which key entry to import. These Green Power keys are indexed keys, - * and there are EMBER_GP_PROXY_TABLE_SIZE/EMBER_GP_SINK_TABLE_SIZE many of them. - * - * For all other key types, both context->key_index and context->eui64 are not - * used. - * - * @param context sl_zb_sec_man_context_t* [IN] The context to set. The context dictates which key type - * to save, key_index (if applicable) into the relevant key storage, eui64 (if - * applicable), etc. - * @param plaintext_key sl_zb_sec_man_key_t* [IN] The key to import. - * @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. - */ - // NOTE: added for good measure - if (context.coreKeyType === SecManKeyType.INTERNAL) { - console.assert(false, `ezspImportKey cannot use INTERNAL key type.`); - return SLStatus.INVALID_PARAMETER; - } - - this.startCommand(EzspFrameID.IMPORT_KEY); - this.buffalo.writeSecManContext(context); - this.buffalo.writeSecManKey(key); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = this.buffalo.readUInt32(); - - return status; - } - - /** - * Callback - * A callback to inform the application that the Network Key has been updated - * and the node has been switched over to use the new key. The actual key being - * used is not passed up, but the sequence number is. - * @param sequenceNumber uint8_t The sequence number of the new network key. - */ - ezspSwitchNetworkKeyHandler(sequenceNumber: number): void { - debug(`ezspSwitchNetworkKeyHandler(): callback called with: [sequenceNumber=${sequenceNumber}]`); - } - - /** - * This function searches through the Key Table and tries to find the entry that - * matches the passed search criteria. - * @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. - */ - async ezspFindKeyTableEntry(address: EmberEUI64, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const index = this.buffalo.readUInt8(); - - return index; - } - - /** - * This function sends an APS TransportKey command containing the current trust - * center link key. The node to which the command is sent is specified via the - * 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 - */ - async ezspSendTrustCenterLinkKey(destinationNodeId: EmberNodeId, destinationEui64: EmberEUI64): Promise { - this.startCommand(EzspFrameID.SEND_TRUST_CENTER_LINK_KEY); - this.buffalo.writeUInt16(destinationNodeId); - this.buffalo.writeIeeeAddr(destinationEui64); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async ezspEraseKeyTableEntry(index: number): Promise { - this.startCommand(EzspFrameID.ERASE_KEY_TABLE_ENTRY); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This function clears the key table of the current network. - * @returns ::EMBER_SUCCESS if the key table was successfully cleared. - * ::EMBER_INVALID_CALL otherwise. - */ - async ezspClearKeyTable(): Promise { - this.startCommand(EzspFrameID.CLEAR_KEY_TABLE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * A function to request a Link Key from the Trust Center with another device on - * the Network (which could be the Trust Center). A Link Key with the Trust - * Center is possible but the requesting device cannot be the Trust Center. Link - * Keys are optional in ZigBee Standard Security and thus the stack cannot know - * whether the other device supports them. If EMBER_REQUEST_KEY_TIMEOUT is - * non-zero on the Trust Center and the partner device is not the Trust Center, - * both devices must request keys with their partner device within the time - * period. The Trust Center only supports one outstanding key request at a time - * and therefore will ignore other requests. If the timeout is zero then the - * Trust Center will immediately respond and not wait for the second request. - * The Trust Center will always immediately respond to requests for a Link Key - * with it. Sleepy devices should poll at a higher rate until a response is - * received or the request times out. The success or failure of the request is - * 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. - */ - async ezspRequestLinkKey(partner: EmberEUI64): Promise { - this.startCommand(EzspFrameID.REQUEST_LINK_KEY); - this.buffalo.writeIeeeAddr(partner); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Requests a new link key from the Trust Center. This function starts by - * sending a Node Descriptor request to the Trust Center to verify its R21+ - * stack version compliance. A Request Key message will then be sent, followed - * by a Verify Key Confirm message. - * @param maxAttempts uint8_t The maximum number of attempts a node should make when sending the Node Descriptor, - * 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. - */ - async ezspUpdateTcLinkKey(maxAttempts: number): Promise { - this.startCommand(EzspFrameID.UPDATE_TC_LINK_KEY); - this.buffalo.writeUInt8(maxAttempts); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * This is a callback that indicates the success or failure of an attempt to establish a key with a partner device. - * @param partner This is the IEEE address of the partner that the device successfully established a key with. - * 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 { - debug(`ezspZigbeeKeyEstablishmentHandler(): callback called with: [partner=${partner}], [status=${EmberKeyStatus[status]}]`); - // 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) - // and always if status=TC_REQUESTER_VERIFY_KEY_FAILURE - } - - /** - * Clear all of the transient link keys from RAM. - */ - async ezspClearTransientLinkKeys(): Promise { - this.startCommand(EzspFrameID.CLEAR_TRANSIENT_LINK_KEYS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Retrieve information about the current and alternate network key, excluding their contents. - * @returns Success or failure of retrieving network key info. - * @returns sl_zb_sec_man_network_key_info_t * Information about current and alternate network keys. - */ - async ezspGetNetworkKeyInfo(): Promise<[SLStatus, networkKeyInfo: SecManNetworkKeyInfo]> { - /** - * Retrieve information about the network key and alternate network key. - * It will not retrieve the actual network key contents. - * - * @param network_key_info sl_zb_sec_man_network_key_info_t* [OUT] The network key info struct used to store network key metadata, - * 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 - * - */ - this.startCommand(EzspFrameID.GET_NETWORK_KEY_INFO); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = this.buffalo.readUInt32(); - const networkKeyInfo = this.buffalo.readSecManNetworkKeyInfo(); - - return [status, networkKeyInfo]; - } - - /** - * 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 - * @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 ]> { - /** - * Retrieve metadata about an APS key. - * It does not retrieve the actual key contents. - * - * @param context sl_zb_sec_man_context_t* [IN/OUT] The context to use to look up a key entry. If the - * user calls this function with the ::ZB_SEC_MAN_FLAG_KEY_INDEX_IS_VALID bit - * set in the context->flag field, then the key_index field in the context - * argument dictates which entry to retrieve. For keys with timeout and - * application link keys, the key_index retrieves the indexed entry into the - * respective table. Upon success, the eui64 field in the context is updated. - * If the user calls this function with the - * ::ZB_SEC_MAN_FLAG_EUI_IS_VALID bit set in the - * context->flag field, then the eui64 field in the context argument - * dictates which entry to retrieve. If the context->core_key_type argument is - * set to SL_ZB_SEC_MAN_KEY_TYPE_NETWORK, an error is returned as network keys - * are not tied to any specific EUI. - * If neither the ::ZB_SEC_MAN_FLAG_KEY_INDEX_IS_VALID bit nor the - * ::ZB_SEC_MAN_FLAG_EUI_IS_VALID bit is set in context->flags, then an error - * will be returned by this function. - * Upon success in fetching a key, the other fields in this argument are - * 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. - * - * @return 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 - * 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 - * EMBER_GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE have not been set in the - * initial security state). - */ - this.startCommand(EzspFrameID.GET_APS_KEY_INFO); - this.buffalo.writeSecManContext(context_in); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const eui: EmberEUI64 = this.buffalo.readIeeeAddr(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - - return [eui, keyData, status]; - } - - /** - * Import an application link key into the key table. - * @param index uint8_t Index where this key is to be imported to. - * @param address EUI64 this key is associated with. - * @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 { - /** - * Import a link key, or SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK key, into storage. - * - * @param index uint8_t [IN] The index to set or overwrite in the key table for keys of - * type SL_ZB_SEC_MAN_KEY_TYPE_APP_LINK. If index is set to 0xFF (255), then - * 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 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. - * - */ - this.startCommand(EzspFrameID.IMPORT_LINK_KEY); - this.buffalo.writeUInt8(index); - this.buffalo.writeIeeeAddr(address); - this.buffalo.writeSecManKey(plaintextKey); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = 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_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]> { - /** - * Export an APS link key by index. - * - * @param index uint8_t - * @param address EmberEUI64 - * @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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[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]; - } - - /** - * 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_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]> { - /** - * 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 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* - */ - this.startCommand(EzspFrameID.EXPORT_LINK_KEY_BY_EUI); - this.buffalo.writeIeeeAddr(eui); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[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]; - } - - /** - * Check whether a key context can be used to load a valid key. - * @param context sl_zb_sec_man_context_t * Context struct to check the validity of. - * @returns Validity of the checked context. - */ - async ezspCheckKeyContext(context: SecManContext): Promise { - /** - * Check that the passed key exists and can be successfully loaded. - * This function does not actually load the context, but only checks that it can be loaded. - * - * @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. - */ - this.startCommand(EzspFrameID.CHECK_KEY_CONTEXT); - this.buffalo.writeSecManContext(context); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = this.buffalo.readUInt32(); - return status; - } - - /** - * 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. - * @returns Status of key import operation. - */ - async ezspImportTransientKey(eui64: EmberEUI64, plaintextKey: SecManKey, flags: SecManFlag): Promise { - /** - * @brief Add a transient or temporary key entry to key storage. - * A key entry added with this API is timed out after - * ::EMBER_TRANSIENT_KEY_TIMEOUT_S seconds, unless the key entry was added using - * the Network Creator Security component, in which case the key will time out - * 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 (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: SLStatus = this.buffalo.readUInt32(); - - return status; - } - - /** - * Export a transient link key from a given table index. - * @param uint8_t Index to export from. - * @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. - */ - 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. - */ - this.startCommand(EzspFrameID.EXPORT_TRANSIENT_KEY_BY_INDEX); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const context = this.buffalo.readSecManContext(); - const plaintextKey = this.buffalo.readSecManKey(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - - return [context, plaintextKey, keyData, status]; - } - - /** - * Export a transient link key associated with a given EUI64 - * @param eui Index to export from. - * @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. - */ - 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. - */ - this.startCommand(EzspFrameID.EXPORT_TRANSIENT_KEY_BY_EUI); - this.buffalo.writeIeeeAddr(eui); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const context = this.buffalo.readSecManContext(); - const plaintextKey = this.buffalo.readSecManKey(); - const keyData = this.buffalo.readSecManAPSKeyMetadata(); - const status: SLStatus = this.buffalo.readUInt32(); - - return [context, plaintextKey, keyData, status]; - } - - //----------------------------------------------------------------------------- - // Trust Center Frames - //----------------------------------------------------------------------------- - - /** - * Callback - * The NCP used the trust center behavior policy to decide whether to allow a - * new node to join the network. The Host cannot change the current decision, - * but it can change the policy for future decisions using the setPolicy - * command. - * @param newNodeId The Node Id of the node whose status changed - * @param newNodeEui64 The EUI64 of the node whose status changed. - * @param status The status of the node: Secure Join/Rejoin, Unsecure Join/Rejoin, Device left. - * @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 { - debug(`ezspTrustCenterJoinHandler(): callback called with: [newNodeId=${newNodeId}], [newNodeEui64=${newNodeEui64}], ` - + `[status=${EmberDeviceUpdate[status]}], [policyDecision=${EmberJoinDecision[policyDecision]}], ` - + `[parentOfNewNodeId=${parentOfNewNodeId}]`); - // NOTE: this is mostly just passing stuff up to Z2M, so use only one emit for all, let adapter do the rest, no parsing needed - this.emit(EzspEvents.TRUST_CENTER_JOIN, newNodeId, newNodeEui64, status, policyDecision, parentOfNewNodeId); - } - - /** - * This function broadcasts a new encryption key, but does not tell the nodes in - * the network to start using it. To tell nodes to switch to the new key, use - * ezspBroadcastNetworkKeySwitch(). This is only valid for the Trust - * Center/Coordinator. It is up to the application to determine how quickly to - * 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. - */ - async ezspBroadcastNextNetworkKey(key: EmberKeyData): Promise { - this.startCommand(EzspFrameID.BROADCAST_NEXT_NETWORK_KEY); - this.buffalo.writeEmberKeyData(key); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async ezspBroadcastNetworkKeySwitch(): Promise { - this.startCommand(EzspFrameID.BROADCAST_NETWORK_KEY_SWITCH); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This routine processes the passed chunk of data and updates the hash context - * based on it. If the 'finalize' parameter is not set, then the length of the - * data passed in must be a multiple of 16. If the 'finalize' parameter is set - * then the length can be any value up 1-16, and the final hash value will be - * calculated. - * @param context EmberAesMmoHashContext * The hash context to update. - * @param finalize This indicates whether the final hash value should be calculated - * @param data uint8_t * The data to hash. - * @returns The result of the operation - * @returns EmberAesMmoHashContext * The updated hash context. - */ - async ezspAesMmoHash(context: EmberAesMmoHashContext, finalize: boolean, data: Buffer): - Promise<[EmberStatus, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const returnContext = this.buffalo.readEmberAesMmoHashContext(); - - return [status, returnContext]; - } - - /** - * This command sends an APS remove device using APS encryption to the - * destination indicating either to remove itself from the network, or one of - * its children. - * @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 - */ - async ezspRemoveDevice(destShort: EmberNodeId, destLong: EmberEUI64, targetLong: EmberEUI64): Promise { - this.startCommand(EzspFrameID.REMOVE_DEVICE); - this.buffalo.writeUInt16(destShort); - this.buffalo.writeIeeeAddr(destLong); - this.buffalo.writeIeeeAddr(targetLong); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This command will send a unicast transport key message with a new NWK key to - * the specified device. APS encryption using the device's existing link key - * will be used. - * @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 - */ - async ezspUnicastNwkKeyUpdate(destShort: EmberNodeId, destLong: EmberEUI64, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - //----------------------------------------------------------------------------- - // Certificate Based Key Exchange (CBKE) Frames - //----------------------------------------------------------------------------- - - /** - * This call starts the generation of the ECC Ephemeral Public/Private key pair. - * When complete it stores the private key. The results are returned via - * ezspGenerateCbkeKeysHandler(). - */ - async ezspGenerateCbkeKeys(): Promise { - this.startCommand(EzspFrameID.GENERATE_CBKE_KEYS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback by the Crypto Engine indicating that a new ephemeral - * public/private key pair has been generated. The public/private key pair is - * stored on the NCP, but only the associated public key is returned to the - * host. The node's associated certificate is also returned. - * @param status The result of the CBKE operation. - * @param ephemeralPublicKey EmberPublicKeyData * The generated ephemeral public key. - */ - ezspGenerateCbkeKeysHandler(status: EmberStatus, ephemeralPublicKey: EmberPublicKeyData): void { - debug(`ezspGenerateCbkeKeysHandler(): callback called with: [status=${EmberStatus[status]}], [ephemeralPublicKey=${ephemeralPublicKey}]`); - } - - /** - * Calculates the SMAC verification keys for both the initiator and responder - * roles of CBKE using the passed parameters and the stored public/private key - * pair previously generated with ezspGenerateKeysRetrieveCert(). It also stores - * the unverified link key data in temporary storage on the NCP until the key - * establishment is complete. - * @param amInitiator The role of this device in the Key Establishment protocol. - * @param partnerCertificate EmberCertificateData * The key establishment partner's implicit certificate. - * @param partnerEphemeralPublicKey EmberPublicKeyData * The key establishment partner's ephemeral public key - */ - async ezspCalculateSmacs(amInitiator: boolean, partnerCertificate: EmberCertificateData, partnerEphemeralPublicKey: EmberPublicKeyData) - : 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback to indicate that the NCP has finished calculating the Secure - * Message Authentication Codes (SMAC) for both the initiator and responder. The - * associated link key is kept in temporary storage until the host tells the NCP - * to store or discard the key via emberClearTemporaryDataMaybeStoreLinkKey(). - * @param status The Result of the CBKE operation. - * @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 { - debug(`ezspCalculateSmacsHandler(): callback called with: [status=${EmberStatus[status]}], [initiatorSmac=${initiatorSmac}], ` - + `[responderSmac=${responderSmac}]`); - } - - /** - * This call starts the generation of the ECC 283k1 curve Ephemeral - * Public/Private key pair. When complete it stores the private key. The results - * are returned via ezspGenerateCbkeKeysHandler283k1(). - */ - async ezspGenerateCbkeKeys283k1(): Promise { - this.startCommand(EzspFrameID.GENERATE_CBKE_KEYS283K1); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback by the Crypto Engine indicating that a new 283k1 ephemeral - * public/private key pair has been generated. The public/private key pair is - * stored on the NCP, but only the associated public key is returned to the - * host. The node's associated certificate is also returned. - * @param status The result of the CBKE operation. - * @param ephemeralPublicKey EmberPublicKey283k1Data * The generated ephemeral public key. - */ - ezspGenerateCbkeKeysHandler283k1(status: EmberStatus, ephemeralPublicKey: EmberPublicKey283k1Data): void { - debug(`ezspGenerateCbkeKeysHandler283k1(): callback called with: [status=${EmberStatus[status]}], ` - + `[ephemeralPublicKey=${ephemeralPublicKey}]`); - } - - /** - * Calculates the SMAC verification keys for both the initiator and responder - * roles of CBKE for the 283k1 ECC curve using the passed parameters and the - * stored public/private key pair previously generated with - * ezspGenerateKeysRetrieveCert283k1(). It also stores the unverified link key - * data in temporary storage on the NCP until the key establishment is complete. - * @param amInitiator The role of this device in the Key Establishment protocol. - * @param partnerCertificate EmberCertificate283k1Data * The key establishment partner's implicit certificate. - * @param partnerEphemeralPublicKey EmberPublicKey283k1Data * The key establishment partner's ephemeral public key - */ - async ezspCalculateSmacs283k1(amInitiator: boolean, partnerCertificate: EmberCertificate283k1Data, - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * A callback to indicate that the NCP has finished calculating the Secure - * Message Authentication Codes (SMAC) for both the initiator and responder for - * the CBKE 283k1 Library. The associated link key is kept in temporary storage - * until the host tells the NCP to store or discard the key via - * emberClearTemporaryDataMaybeStoreLinkKey(). - * @param status The Result of the CBKE operation. - * @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 { - debug(`ezspCalculateSmacsHandler283k1(): callback called with: [status=${EmberStatus[status]}], [initiatorSmac=${initiatorSmac}], ` - + `[responderSmac=${responderSmac}]`); - } - - /** - * Clears the temporary data associated with CBKE and the key establishment, - * most notably the ephemeral public/private key pair. If storeLinKey is true it - * moves the unverified link key stored in temporary storage into the link key - * table. Otherwise it discards the key. - * @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 { - this.startCommand(EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY); - this.buffalo.writeUInt8(storeLinkKey ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Clears the temporary data associated with CBKE and the key establishment, - * most notably the ephemeral public/private key pair. If storeLinKey is true it - * moves the unverified link key stored in temporary storage into the link key - * table. Otherwise it discards the key. - * @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 { - this.startCommand(EzspFrameID.CLEAR_TEMPORARY_DATA_MAYBE_STORE_LINK_KEY283K1); - this.buffalo.writeUInt8(storeLinkKey ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Retrieves the certificate installed on the NCP. - * @returns EmberCertificateData * The locally installed certificate. - */ - async ezspGetCertificate(): Promise<[EmberStatus, localCert: EmberCertificateData]> { - this.startCommand(EzspFrameID.GET_CERTIFICATE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const localCert = this.buffalo.readEmberCertificateData(); - - return [status, localCert]; - } - - /** - * Retrieves the 283k certificate installed on the NCP. - * @returns EmberCertificate283k1Data * The locally installed certificate. - */ - async ezspGetCertificate283k1(): Promise<[EmberStatus, localCert: EmberCertificate283k1Data]> { - this.startCommand(EzspFrameID.GET_CERTIFICATE283K1); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const localCert = this.buffalo.readEmberCertificate283k1Data(); - - 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: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * The handler that returns the results of the signing operation. On success, - * the signature will be appended to the original message (including the - * signature type indicator that replaced the startIndex field for the signing) - * and both are returned via this callback. - * @param status The result of the DSA signing operation. - * @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 { - debug(`ezspDsaSignHandler(): callback called with: [status=${EmberStatus[status]}], [messageContents=${messageContents.toString('hex')}]`); - } - - /** - * Verify that signature of the associated message digest was signed by the - * private key of the associated certificate. - * @param digest EmberMessageDigest * The AES-MMO message digest of the signed data. - * If dsaSign command was used to generate the signature for this data, the final byte (replaced by signature type of 0x01) - * in the messageContents array passed to dsaSign is included in the hash context used for the digest calculation. - * @param signerCertificate EmberCertificateData * The certificate of the signer. Note that the signer's certificate and the verifier's - * 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 { - this.startCommand(EzspFrameID.DSA_VERIFY); - this.buffalo.writeEmberMessageDigest(digest); - this.buffalo.writeEmberCertificateData(signerCertificate); - this.buffalo.writeEmberSignatureData(receivedSig); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * This callback is executed by the stack when the DSA verification has - * completed and has a result. If the result is EMBER_SUCCESS, the signature is - * valid. If the result is EMBER_SIGNATURE_VERIFY_FAILURE then the signature is - * invalid. If the result is anything else then the signature verify operation - * failed and the validity is unknown. - * @param status The result of the DSA verification operation. - */ - ezspDsaVerifyHandler(status: EmberStatus): void { - debug(`ezspDsaVerifyHandler(): callback called with: [status=${EmberStatus[status]}]`); - } - - /** - * Verify that signature of the associated message digest was signed by the - * private key of the associated certificate. - * @param digest EmberMessageDigest * The AES-MMO message digest of the signed data. - * If dsaSign command was used to generate the signature for this data, the final byte (replaced by signature type of 0x01) - * in the messageContents array passed to dsaSign is included in the hash context used for the digest calculation. - * @param signerCertificate EmberCertificate283k1Data * The certificate of the signer. Note that the signer's certificate and the verifier's - * certificate must both be issued by the same Certificate Authority, so they should share the same CA Public Key. - * @param receivedSig EmberSignature283k1Data * The signature of the signed data. - */ - async ezspDsaVerify283k1(digest: EmberMessageDigest, signerCertificate: EmberCertificate283k1Data, receivedSig: EmberSignature283k1Data) - : Promise { - this.startCommand(EzspFrameID.DSA_VERIFY283K1); - this.buffalo.writeEmberMessageDigest(digest); - this.buffalo.writeEmberCertificate283k1Data(signerCertificate); - this.buffalo.writeEmberSignature283k1Data(receivedSig); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sets the device's CA public key, local certificate, and static private key on - * the NCP associated with this node. - * @param caPublic EmberPublicKeyData * The Certificate Authority's public key. - * @param myCert EmberCertificateData * The node's new certificate signed by the CA. - * @param myKey EmberPrivateKeyData *The node's new static private key. - */ - async ezspSetPreinstalledCbkeData(caPublic: EmberPublicKeyData, myCert: EmberCertificateData, myKey: EmberPrivateKeyData): - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sets the device's 283k1 curve CA public key, local certificate, and static - * private key on the NCP associated with this node. - * @returns Status of operation - */ - async ezspSavePreinstalledCbkeData283k1(): Promise { - this.startCommand(EzspFrameID.SAVE_PREINSTALLED_CBKE_DATA283K1); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - //----------------------------------------------------------------------------- - // Mfglib Frames - //----------------------------------------------------------------------------- - - /** - * Activate use of mfglib test routines and enables the radio receiver to report - * packets it receives to the mfgLibRxHandler() callback. These packets will not - * 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. - */ - async ezspMfglibStart(rxCallback: boolean): Promise { - this.startCommand(EzspFrameID.MFGLIB_START); - this.buffalo.writeUInt8(rxCallback ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async mfglibEnd(): Promise { - this.startCommand(EzspFrameID.MFGLIB_END); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Starts transmitting an unmodulated tone on the currently set channel and - * power level. Upon successful return, the tone will be transmitting. To stop - * 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. - */ - async mfglibStartTone(): Promise { - this.startCommand(EzspFrameID.MFGLIB_START_TONE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async mfglibStartStream(): Promise { - this.startCommand(EzspFrameID.MFGLIB_START_STREAM); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Stops transmitting a random stream of characters started by - * mfglibStartStream(). - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async mfglibStopStream(): Promise { - this.startCommand(EzspFrameID.MFGLIB_STOP_STREAM); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Sends a single packet consisting of the following bytes: packetLength, - * packetContents[0], ... , packetContents[packetLength - 3], CRC[0], CRC[1]. - * The total number of bytes sent is packetLength + 1. The radio replaces the - * 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. - */ - async mfglibSendPacket(packetContents: Buffer): Promise { - this.startCommand(EzspFrameID.MFGLIB_SEND_PACKET); - this.buffalo.writePayload(packetContents); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async mfglibSetChannel(channel: number): Promise { - this.startCommand(EzspFrameID.MFGLIB_SET_CHANNEL); - this.buffalo.writeUInt8(channel); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const channel = this.buffalo.readUInt8(); - return channel; - } - - /** - * First select the transmit power mode, and then include a method for selecting - * the radio transmit power. The valid power settings depend upon the specific - * radio in use. Ember radios have discrete power settings, and then requested - * power is rounded to a valid power setting; the actual power output is - * 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. - */ - async mfglibSetPower(txPowerMode: EmberTXPowerMode, power: number): Promise { - this.startCommand(EzspFrameID.MFGLIB_SET_POWER); - this.buffalo.writeUInt16(txPowerMode); - this.buffalo.writeUInt8(power); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const power = this.buffalo.readUInt8(); - return power; - } - - /** - * Callback - * A callback indicating a packet with a valid CRC has been received. - * @param linkQuality uint8_t The link quality observed during the reception - * @param rssi int8_t The energy level (in units of dBm) observed during the reception. - * @param packetLength uint8_t The length of the packetContents parameter in bytes. Will be greater than 3 and less than 123. - * @param packetContents uint8_t * The received packet (last 2 bytes are not FCS / CRC and may be discarded) - */ - ezspMfglibRxHandler(linkQuality: number, rssi: number, packetLength: number, packetContents: number[]): void { - debug(`ezspMfglibRxHandler(): callback called with: [linkQuality=${linkQuality}], [rssi=${rssi}], ` - + `[packetLength=${packetLength}], [packetContents=${packetContents}]`); - // gecko_sdk_4.4.0\protocol\zigbee\app\framework\plugin\manufacturing-library-cli\manufacturing-library-cli-host.c - } - - //----------------------------------------------------------------------------- - // Bootloader Frames - //----------------------------------------------------------------------------- - - /** - * Quits the current application and launches the standalone bootloader (if - * installed) The function returns an error if the standalone bootloader is not - * present - * @param mode uint8_t Controls the mode in which the standalone bootloader will run. See the app. note for full details. - * Options are: STANDALONE_BOOTLOADER_NORMAL_MODE: Will listen for an over-the-air image transfer on the current - * 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. - */ - async ezspLaunchStandaloneBootloader(mode: number): Promise { - this.startCommand(EzspFrameID.LAUNCH_STANDALONE_BOOTLOADER); - this.buffalo.writeUInt8(mode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Transmits the given bootload message to a neighboring node using a specific - * 802.15.4 header that allows the EmberZNet stack as well as the bootloader to - * recognize the message, but will not interfere with other ZigBee stacks. - * @param broadcast If true, the destination address and pan id are both set to the broadcast address. - * @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. - */ - async ezspSendBootloadMessage(broadcast: boolean, destEui64: EmberEUI64, 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Detects if the standalone bootloader is installed, and if so returns the - * installed version. If not return 0xffff. A returned version of 0x1234 would - * indicate version 1.2 build 34. Also return the node's version of PLAT, MICRO - * and PHY. - * @returns uint16_t BOOTLOADER_INVALID_VERSION if the standalone bootloader is not present, - * or the version of the installed standalone bootloader. - * @returns uint8_t * The value of PLAT on the node - * @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]> { - this.startCommand(EzspFrameID.GET_STANDALONE_BOOTLOADER_VERSION_PLAT_MICRO_PHY); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const bootloader_version = this.buffalo.readUInt16(); - const nodePlat = this.buffalo.readUInt8(); - const nodeMicro = this.buffalo.readUInt8(); - const nodePhy = this.buffalo.readUInt8(); - - return [bootloader_version, nodePlat, nodeMicro, nodePhy]; - } - - /** - * Callback - * 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 messageContents uint8_t *The bootload message that was sent. - */ - ezspIncomingBootloadMessageHandler(longId: EmberEUI64, lastHopLqi: number, lastHopRssi: number, messageContents: Buffer): void { - debug(`ezspIncomingBootloadMessageHandler(): callback called with: [longId=${longId}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}], [messageContents=${messageContents.toString('hex')}]`); - } - - /** - * 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 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 { - debug(`ezspBootloadTransmitCompleteHandler(): callback called with: [status=${EmberStatus[status]}], ` - + `[messageContents=${messageContents.toString('hex')}]`); - } - - /** - * Perform AES encryption on plaintext using key. - * @param uint8_t * 16 bytes of plaintext. - * @param uint8_t * The 16-byte encryption key to use. - * @returns uint8_t * 16 bytes of ciphertext. - */ - async ezspAesEncrypt(plaintext: number[], key: number[]): Promise { - this.startCommand(EzspFrameID.AES_ENCRYPT); - this.buffalo.writeListUInt8(plaintext); - this.buffalo.writeListUInt8(key); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const ciphertext = this.buffalo.readListUInt8({length: 16}); - - return ciphertext; - } - - //----------------------------------------------------------------------------- - // ZLL Frames - //----------------------------------------------------------------------------- - - /** - * A consolidation of ZLL network operations with similar signatures; - * specifically, forming and joining networks or touch-linking. - * @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. - */ - 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.writeUInt8(radioTxPower); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This call will cause the device to setup the security information used in its - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This call will update ZLL security token information. Unlike - * 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. - */ - async ezspZllSetSecurityStateWithoutKey(securityState: EmberZllInitialSecurityState): Promise { - this.startCommand(EzspFrameID.ZLL_SET_SECURITY_STATE_WITHOUT_KEY); - this.buffalo.writeEmberZllInitialSecurityState(securityState); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * This call will initiate a ZLL network scan on all the specified channels. - * @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. - */ - async ezspZllStartScan(channelMask: number, radioPowerForScan: number, nodeType: EmberNodeType): Promise { - this.startCommand(EzspFrameID.ZLL_START_SCAN); - this.buffalo.writeUInt32(channelMask); - this.buffalo.writeUInt8(radioPowerForScan); - this.buffalo.writeUInt8(nodeType); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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. - */ - async ezspZllSetRxOnWhenIdle(durationMs: number): Promise { - this.startCommand(EzspFrameID.ZLL_SET_RX_ON_WHEN_IDLE); - this.buffalo.writeUInt32(durationMs); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Callback - * This call is fired when a ZLL network scan finds a ZLL network. - * @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. - */ - ezspZllNetworkFoundHandler(networkInfo: EmberZllNetwork, isDeviceInfoNull: boolean, deviceInfo: EmberZllDeviceInfoRecord, - lastHopLqi: number, lastHopRssi: number): void { - debug(`ezspZllNetworkFoundHandler(): callback called with: [networkInfo=${networkInfo}], [isDeviceInfoNull=${isDeviceInfoNull}], ` - + `[deviceInfo=${deviceInfo}], [lastHopLqi=${lastHopLqi}], [lastHopRssi=${lastHopRssi}]`); - } - - /** - * Callback - * This call is fired when a ZLL network scan is complete. - * @param status Status of the operation. - */ - ezspZllScanCompleteHandler(status: EmberStatus): void { - debug(`ezspZllScanCompleteHandler(): callback called with: [status=${EmberStatus[status]}]`); - } - - /** - * Callback - * 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. - */ - ezspZllAddressAssignmentHandler(addressInfo: EmberZllAddressAssignment, lastHopLqi: number, lastHopRssi: number): void { - debug(`ezspZllAddressAssignmentHandler(): callback called with: [addressInfo=${addressInfo}], [lastHopLqi=${lastHopLqi}], ` - + `[lastHopRssi=${lastHopRssi}]`); - } - - /** - * Callback - * This call is fired when the device is a target of a touch link. - * @param networkInfo EmberZllNetwork * Information about the network. - */ - ezspZllTouchLinkTargetHandler(networkInfo: EmberZllNetwork): void { - debug(`ezspZllTouchLinkTargetHandler(): callback called with: [networkInfo=${networkInfo}]`); - } - - /** - * Get the ZLL tokens. - * @returns EmberTokTypeStackZllData * Data token return value. - * @returns EmberTokTypeStackZllSecurity * Security token return value. - */ - async ezspZllGetTokens(): Promise<[data: EmberTokTypeStackZllData, security: EmberTokTypeStackZllSecurity]> { - this.startCommand(EzspFrameID.ZLL_GET_TOKENS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const data = this.buffalo.readEmberTokTypeStackZllData(); - const security = this.buffalo.readEmberTokTypeStackZllSecurity(); - - return [data, security]; - } - - /** - * Set the ZLL data token. - * @param data EmberTokTypeStackZllData * Data token to be set. - */ - async ezspZllSetDataToken(data: EmberTokTypeStackZllData): Promise { - this.startCommand(EzspFrameID.ZLL_SET_DATA_TOKEN); - this.buffalo.writeEmberTokTypeStackZllData(data); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Set the ZLL data token bitmask to reflect the ZLL network state. - */ - async ezspZllSetNonZllNetwork(): Promise { - this.startCommand(EzspFrameID.ZLL_SET_NON_ZLL_NETWORK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Is this a ZLL network? - * @returns ZLL network? - */ - async ezspIsZllNetwork(): Promise { - this.startCommand(EzspFrameID.IS_ZLL_NETWORK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const isZllNetwork = this.buffalo.readUInt8() === 1 ? true : false; - - return isZllNetwork; - } - - /** - * This call sets the radio's default idle power mode. - * @param mode The power mode to be set. - */ - async ezspZllSetRadioIdleMode(mode: number): Promise { - this.startCommand(EzspFrameID.ZLL_SET_RADIO_IDLE_MODE); - this.buffalo.writeUInt8(mode); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * This call gets the radio's default idle power mode. - * @returns uint8_t The current power mode. - */ - async ezspZllGetRadioIdleMode(): Promise { - this.startCommand(EzspFrameID.ZLL_GET_RADIO_IDLE_MODE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const radioIdleMode = this.buffalo.readUInt8(); - - return radioIdleMode; - } - - /** - * This call sets the default node type for a factory new ZLL device. - * @param nodeType The node type to be set. - */ - async ezspSetZllNodeType(nodeType: EmberNodeType): Promise { - this.startCommand(EzspFrameID.SET_ZLL_NODE_TYPE); - this.buffalo.writeUInt8(nodeType); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * This call sets additional capability bits in the ZLL state. - * @param uint16_t A mask with the bits to be set or cleared. - */ - async ezspSetZllAdditionalState(state: number): Promise { - this.startCommand(EzspFrameID.SET_ZLL_ADDITIONAL_STATE); - this.buffalo.writeUInt16(state); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Is there a ZLL (Touchlink) operation in progress? - * @returns ZLL operation in progress? false on error - */ - async ezspZllOperationInProgress(): Promise { - this.startCommand(EzspFrameID.ZLL_OPERATION_IN_PROGRESS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const zllOperationInProgress = this.buffalo.readUInt8() === 1 ? true : false; - return zllOperationInProgress; - } - - /** - * Is the ZLL radio on when idle mode is active? - * @returns ZLL radio on when idle mode is active? false on error - */ - async ezspZllRxOnWhenIdleGetActive(): Promise { - this.startCommand(EzspFrameID.ZLL_RX_ON_WHEN_IDLE_GET_ACTIVE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const zllRxOnWhenIdleGetActive = this.buffalo.readUInt8() === 1 ? true : false; - - return zllRxOnWhenIdleGetActive; - } - - /** - * Informs the ZLL API that application scanning is complete - */ - async ezspZllScanningComplete(): Promise { - this.startCommand(EzspFrameID.ZLL_SCANNING_COMPLETE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Get the primary ZLL (touchlink) channel mask. - * @returns uint32_t The primary ZLL channel mask - */ - async ezspGetZllPrimaryChannelMask(): Promise { - this.startCommand(EzspFrameID.GET_ZLL_PRIMARY_CHANNEL_MASK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const zllPrimaryChannelMask = this.buffalo.readUInt32(); - - return zllPrimaryChannelMask; - } - - /** - * Get the secondary ZLL (touchlink) channel mask. - * @returns uint32_t The secondary ZLL channel mask - */ - async ezspGetZllSecondaryChannelMask(): Promise { - this.startCommand(EzspFrameID.GET_ZLL_SECONDARY_CHANNEL_MASK); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const zllSecondaryChannelMask = this.buffalo.readUInt32(); - - return zllSecondaryChannelMask; - } - - /** - * Set the primary ZLL (touchlink) channel mask - * @param uint32_t The primary ZLL channel mask - */ - async ezspSetZllPrimaryChannelMask(zllPrimaryChannelMask: number): Promise { - - this.startCommand(EzspFrameID.SET_ZLL_PRIMARY_CHANNEL_MASK); - this.buffalo.writeUInt32(zllPrimaryChannelMask); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Set the secondary ZLL (touchlink) channel mask. - * @param uint32_t The secondary ZLL channel mask - */ - async ezspSetZllSecondaryChannelMask(zllSecondaryChannelMask: number): Promise { - - this.startCommand(EzspFrameID.SET_ZLL_SECONDARY_CHANNEL_MASK); - this.buffalo.writeUInt32(zllSecondaryChannelMask); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Clear ZLL stack tokens. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const isHubConnected = this.buffalo.readUInt8() === 1 ? true : false; - - return isHubConnected; - } - - //----------------------------------------------------------------------------- - // Green Power Frames - //----------------------------------------------------------------------------- - - /** - * Update the GP Proxy table based on a GP pairing. - * @param options uint32_t The options field of the GP Pairing command. - * @param addr EmberGpAddress * The target GPD. - * @param commMode uint8_t The communication mode of the GP Sink. - * @param sinkNetworkAddress uint16_t The network address of the GP Sink. - * @param sinkGroupId uint16_t The group ID of the GP Sink. - * @param assignedAlias uint16_t The alias assigned to the GPD. - * @param sinkIeeeAddress uint8_t * The IEEE address of the GP Sink. - * @param gpdKey EmberKeyData * The key to use for the target GPD. - * @param gpdSecurityFrameCounter uint32_t The GPD security frame counter. - * @param forwardingRadius uint8_t The forwarding radius. - * @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, - forwardingRadius: number): Promise { - this.startCommand(EzspFrameID.GP_PROXY_TABLE_PROCESS_GP_PAIRING); - this.buffalo.writeUInt32(options); - this.buffalo.writeEmberGpAddress(addr); - this.buffalo.writeUInt8(commMode); - this.buffalo.writeUInt16(sinkNetworkAddress); - this.buffalo.writeUInt16(sinkGroupId); - this.buffalo.writeUInt16(assignedAlias); - this.buffalo.writeIeeeAddr(sinkIeeeAddress); - this.buffalo.writeEmberKeyData(gpdKey); - this.buffalo.writeUInt32(gpdSecurityFrameCounter); - this.buffalo.writeUInt8(forwardingRadius); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const gpPairingAdded = this.buffalo.readUInt8() === 1 ? true : false; - - return gpPairingAdded; - } - - /** - * Adds/removes an entry from the GP Tx Queue. - * @param action The action to perform on the GP TX queue (true to add, false to remove). - * @param useCca Whether to use ClearChannelAssessment when transmitting the GPDF. - * @param addr EmberGpAddress * The Address of the destination GPD. - * @param gpdCommandId uint8_t The GPD command ID to send. - * @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. - */ - async ezspDGpSend(action: boolean, useCca: boolean, addr: EmberGpAddress, gpdCommandId: number, gpdAsdu: Buffer, - gpepHandle: number, gpTxQueueEntryLifetimeMs: number): Promise { - this.startCommand(EzspFrameID.D_GP_SEND); - this.buffalo.writeUInt8(action ? 1 : 0); - this.buffalo.writeUInt8(useCca ? 1 : 0); - this.buffalo.writeEmberGpAddress(addr); - this.buffalo.writeUInt8(gpdCommandId); - this.buffalo.writePayload(gpdAsdu); - this.buffalo.writeUInt8(gpepHandle); - this.buffalo.writeUInt16(gpTxQueueEntryLifetimeMs); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * 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 gpepHandle uint8_t The handle of the GPDF. - */ - ezspDGpSentHandler(status: EmberStatus, gpepHandle: number): void { - debug(`ezspDGpSentHandler(): callback called with: [status=${EmberStatus[status]}], [gpepHandle=${gpepHandle}]`); - } - - /** - * Callback - * A callback invoked by the ZigBee GP stack when a GPDF is received. - * @param status The status of the GPDF receive. - * @param gpdLink uint8_t The gpdLink value of the received GPDF. - * @param sequenceNumber uint8_t The GPDF sequence number. - * @param addr EmberGpAddress *The address of the source GPD. - * @param gpdfSecurityLevel The security level of the received GPDF. - * @param gpdfSecurityKeyType The securityKeyType used to decrypt/authenticate the incoming GPDF. - * @param autoCommissioning Whether the incoming GPDF had the auto-commissioning bit set. - * @param bidirectionalInfo uint8_t Bidirectional information represented in bitfields, - * where bit0 holds the rxAfterTx of incoming gpdf and bit1 holds if tx queue is available for outgoing gpdf. - * @param gpdSecurityFrameCounter uint32_t The security frame counter of the incoming GDPF. - * @param gpdCommandId uint8_t The gpdCommandId of the incoming GPDF. - * @param mic uint32_t The received MIC of the GPDF. - * @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, - gpdfSecurityLevel: EmberGpSecurityLevel, gpdfSecurityKeyType: EmberGpKeyType, autoCommissioning: boolean, bidirectionalInfo: number, - gpdSecurityFrameCounter: number, gpdCommandId: number, mic: number, proxyTableIndex: number, gpdCommandPayload: Buffer): void { - debug(`ezspGpepIncomingMessageHandler(): callback called with: [status=${EmberStatus[status]}], [gpdLink=${gpdLink}], ` - + `[sequenceNumber=${sequenceNumber}], [addr=${addr}], [gpdfSecurityLevel=${gpdfSecurityLevel}], ` - + `[gpdfSecurityKeyType=${gpdfSecurityKeyType}], [autoCommissioning=${autoCommissioning}], [bidirectionalInfo=${bidirectionalInfo}], ` - + `[gpdSecurityFrameCounter=${gpdSecurityFrameCounter}], [gpdCommandId=${gpdCommandId}], [mic=${mic}], ` - + `[proxyTableIndex=${proxyTableIndex}], [gpdCommandPayload=${gpdCommandPayload}]`); - - // TODO: triple-checking required here - if (gpdCommandPayload.length) { - const gpdBuffalo = new EzspBuffalo(gpdCommandPayload, 0); - - switch (gpdCommandId) { - case 0xE0: { - // commissioning notification - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const st = gpdBuffalo.readUInt8(); - const deviceId = gpdBuffalo.readUInt8(); - const options = gpdBuffalo.readUInt8(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const extOptions = gpdBuffalo.readUInt8(); - const key = gpdBuffalo.readEmberKeyData(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const mic = gpdBuffalo.readUInt32(); - const counter = gpdBuffalo.readUInt32(); - - this.emit( - EzspEvents.GREENPOWER_MESSAGE, - (addr.applicationId === EmberGpApplicationId.SOURCE_ID) ? addr.sourceId : addr.gpdIeeeAddress, - gpdCommandId, - gpdLink, - sequenceNumber, - deviceId, - options, - key, - counter - ); - break; - } - default: { - // notification - this.emit( - EzspEvents.GREENPOWER_MESSAGE, - (addr.applicationId === EmberGpApplicationId.SOURCE_ID) ? addr.sourceId : addr.gpdIeeeAddress, - gpdCommandId, - gpdLink, - sequenceNumber, - null, - null, - null, - null - ); - break; - } - } - } - } - - /** - * 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 EmberGpProxyTableEntry * An EmberGpProxyTableEntry struct containing a copy of the requested proxy entry. - */ - async ezspGpProxyTableGetEntry(proxyIndex: number): Promise<[EmberStatus, entry: EmberGpProxyTableEntry]> { - this.startCommand(EzspFrameID.GP_PROXY_TABLE_GET_ENTRY); - this.buffalo.writeUInt8(proxyIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const entry = this.buffalo.readEmberGpProxyTableEntry(); - - return [status, entry]; - } - - /** - * Finds the index of the passed address in the gp table. - * @param addr EmberGpAddress * The address to search for - * @returns uint8_t The index, or 0xFF for not found - */ - async ezspGpProxyTableLookup(addr: EmberGpAddress): Promise { - this.startCommand(EzspFrameID.GP_PROXY_TABLE_LOOKUP); - this.buffalo.writeEmberGpAddress(addr); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const index = this.buffalo.readUInt8(); - - return index; - } - - /** - * 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 EmberGpSinkTableEntry * An EmberGpSinkTableEntry struct containing a copy of the requested sink entry. - */ - async ezspGpSinkTableGetEntry(sinkIndex: number): Promise<[EmberStatus, entry: EmberGpSinkTableEntry]> { - this.startCommand(EzspFrameID.GP_SINK_TABLE_GET_ENTRY); - this.buffalo.writeUInt8(sinkIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const entry = this.buffalo.readEmberGpSinkTableEntry(); - - return [status, entry]; - } - - /** - * Finds the index of the passed address in the gp table. - * @param addr EmberGpAddress *The address to search for. - * @returns uint8_t The index, or 0xFF for not found - */ - async ezspGpSinkTableLookup(addr: EmberGpAddress): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_LOOKUP); - this.buffalo.writeEmberGpAddress(addr); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const index = this.buffalo.readUInt8(); - - return index; - } - - /** - * 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Removes the sink table entry stored at the passed index. - * @param uint8_t The index of the requested sink table entry. - */ - async ezspGpSinkTableRemoveEntry(sinkIndex: number): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_REMOVE_ENTRY); - this.buffalo.writeUInt8(sinkIndex); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Finds or allocates a sink entry - * @param addr EmberGpAddress * An EmberGpAddress struct containing a copy of the gpd address to be found. - * @returns uint8_t An index of found or allocated sink or 0xFF if failed. - */ - async ezspGpSinkTableFindOrAllocateEntry(addr: EmberGpAddress): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_FIND_OR_ALLOCATE_ENTRY); - this.buffalo.writeEmberGpAddress(addr); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const index = this.buffalo.readUInt8(); - - return index; - } - - /** - * Clear the entire sink table - */ - async ezspGpSinkTableClearAll(): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_CLEAR_ALL); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Iniitializes Sink Table - */ - async ezspGpSinkTableInit(): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_INIT); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Sets security framecounter in the sink table - * @param index uint8_t Index to the Sink table - * @param sfc uint32_t Security Frame Counter - */ - async ezspGpSinkTableSetSecurityFrameCounter(index: number, sfc: number): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_SET_SECURITY_FRAME_COUNTER); - this.buffalo.writeUInt8(index); - this.buffalo.writeUInt32(sfc); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Puts the GPS in commissioning mode. - * @param uint8_t commissioning options - * @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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Clears all entries within the translation table. - */ - async ezspGpTranslationTableClear(): Promise { - this.startCommand(EzspFrameID.GP_TRANSLATION_TABLE_CLEAR); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Return number of active entries in sink table. - * @returns uint8_t Number of active entries in sink table. 0 if error. - */ - async ezspGpSinkTableGetNumberOfActiveEntries(): Promise { - this.startCommand(EzspFrameID.GP_SINK_TABLE_GET_NUMBER_OF_ACTIVE_ENTRIES); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const number_of_entries = this.buffalo.readUInt8(); - - return number_of_entries; - } - - //----------------------------------------------------------------------------- - // Token Interface Frames - //----------------------------------------------------------------------------- - - /** - * Gets the total number of tokens. - * @returns uint8_t Total number of tokens. - */ - async ezspGetTokenCount(): Promise { - this.startCommand(EzspFrameID.GET_TOKEN_COUNT); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const count = this.buffalo.readUInt8(); - - return count; - } - - /** - * 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 EmberTokenInfo * Token information. - */ - async ezspGetTokenInfo(index: number): Promise<[EmberStatus, tokenInfo: EmberTokenInfo]> { - this.startCommand(EzspFrameID.GET_TOKEN_INFO); - this.buffalo.writeUInt8(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const tokenInfo = this.buffalo.readEmberTokenInfo(); - - return [status, tokenInfo]; - } - - /** - * 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 EmberTokenData * Token Data - */ - async ezspGetTokenData(token: number, index: number): Promise<[EmberStatus, tokenData: EmberTokenData]> { - this.startCommand(EzspFrameID.GET_TOKEN_DATA); - this.buffalo.writeUInt32(token); - this.buffalo.writeUInt32(index); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - const tokenData = this.buffalo.readEmberTokenData(); - - return [status, tokenData]; - } - - /** - * Sets 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 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. - */ - 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(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Reset the node by calling halReboot. - */ - async ezspResetNode(): Promise { - this.startCommand(EzspFrameID.RESET_NODE); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } - - /** - * Run GP security test vectors. - * @returns An EmberStatus value indicating success or the reason for failure. - */ - async ezspGpSecurityTestVectors(): Promise { - this.startCommand(EzspFrameID.GP_SECURITY_TEST_VECTORS); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - - const status: EmberStatus = this.buffalo.readUInt8(); - return status; - } - - /** - * Factory reset all configured zigbee tokens - * @param excludeOutgoingFC Exclude network and APS outgoing frame counter tokens. - * @param excludeBootCounter Exclude stack boot counter token. - */ - async ezspTokenFactoryReset(excludeOutgoingFC: boolean, excludeBootCounter: boolean): Promise { - this.startCommand(EzspFrameID.TOKEN_FACTORY_RESET); - this.buffalo.writeUInt8(excludeOutgoingFC ? 1 : 0); - this.buffalo.writeUInt8(excludeBootCounter ? 1 : 0); - - const sendStatus: EzspStatus = await this.sendCommand(); - - if (sendStatus !== EzspStatus.SUCCESS) { - throw new Error(EzspStatus[sendStatus]); - } - } -} diff --git a/src/adapter/ember/types.ts b/src/adapter/ember/types.ts deleted file mode 100644 index 03126b944aa..00000000000 --- a/src/adapter/ember/types.ts +++ /dev/null @@ -1,812 +0,0 @@ -import { - EmberApsOption, - EmberBindingType, - EmberCurrentSecurityBitmask, - EmberGpApplicationId, - EmberGpProxyTableEntryStatus, - EmberGpSinkTableEntryStatus, - EmberGpSinkType, - EmberJoinMethod, - EmberKeyStructBitmask, - EmberNetworkInitBitmask, - EmberNodeType, - EmberVersionType, - EmberZllKeyIndex, - EmberZllState, - SecManDerivedKeyType, - SecManFlag, - SecManKeyType -} 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; -/** - * The percent of duty cycle for a limit. - * - * Duty cycle, limits, and thresholds are reported in units of - * percent * 100 (i.e., 10000 = 100.00%, 1 = 0.01%). - * uint16_t - */ -export type EmberDutyCycleHectoPct = number; -/** Refer to the Zigbee application profile ID. uint16_t */ -export type ProfileId = number; -/** Refer to the ZCL cluster ID. uint16_t */ -export type ClusterId = number; - - -/** A version structure containing all version information. */ -export type EmberVersion = { - /** - * A unique build number generated by Silicon Labs' internal build engineering process - * - * uint16_t - */ - build: number, - /** - * Major version number - * (used to indicate major architectural changes or significant supported platform changes). - * - * A.b.c.d - * uint8_t - */ - major: number, - /** - * Minor version number - * (used to indicate significant new features, API changes; not always code-compatible with previous minor versions). - * - * a.B.c.d - * uint8_t - */ - minor: number, - /** - * Patch (sub-minor) version number - * (used to indicate bug fixes or minor features that don't affect code-compatibility with previous application code). - * - * a.b.C.d - * uint8_t - */ - patch: number, - /** - * Special version number - * (used to indicate superficial changes that don't require re-certification of the stack as a ZigBee-Compliant Platform, - * such as changes that only affect installer packaging, documentation, or comments in the code) - * - * a.b.c.D - * uint8_t - */ - special: number, - /** - * Corresponding to an enum value from EmberVersionType. - * - * Pre-release, Alpha, Beta, GA - */ - type: EmberVersionType, -}; - -/** Defines the network initialization configuration that should be used when ::emberNetworkInit() is called by the application. */ -export type EmberNetworkInitStruct = { - bitmask: EmberNetworkInitBitmask, -}; - -/** - * Holds network parameters. - * - * For information about power settings and radio channels, see the technical specification for the RF communication module in your Developer Kit. - */ -export type EmberNetworkParameters = { - /** The network's extended PAN identifier. int8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, - /** The network's PAN identifier. uint16_t*/ - panId: EmberPanId, - /** A power setting, in dBm. int8_t*/ - radioTxPower: number, - /** A radio channel. Be sure to specify a channel supported by the radio. uint8_t */ - radioChannel: number, - /** - * Join method: The protocol messages used to establish an initial parent. - * It is ignored when forming a ZigBee network, or when querying the stack for its network parameters. - */ - joinMethod: EmberJoinMethod, - /** - * 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, - /** - * 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 - * ID or channel change. This may only be set at joining when using - * EMBER_USE_CONFIGURED_NWK_STATE as the join method. - * uint8_t - */ - nwkUpdateId: number, - /** - * The NWK channel mask. The list of preferred channels that the NWK manager - * has told this device to use when searching for the network. - * This may only be set at joining when using EMBER_USE_CONFIGURED_NWK_STATE as the join method. - * uint32_t - */ - channels: number, -}; - -/** Defines a beacon entry that is processed when scanning, joining, or rejoining. */ -export type EmberBeaconData = { - panId: EmberPanId, - sender: EmberNodeId, - /** uint8_t */ - channel: number, - /** uint8_t */ - lqi: number, - /** int8_t */ - rssi: number, - /** uint8_t */ - depth: number, - /** uint8_t */ - nwkUpdateId: number, - /** Only valid if enhanced beacon. int8_t */ - power: number, - /** TC connectivity and long uptime from capacity field. int8_t */ - parentPriority: number, - /** uint8_t */ - supportedKeyNegotiationMethods: number, - extended_beacon: boolean, - /** Enhanced or regular beacon. default true */ - enhanced: boolean, - /** default true */ - permitJoin: boolean, - /** default true */ - hasCapacity: boolean, - /** default true */ - tcConnectivity: boolean, - /** default true */ - longUptime: boolean, - /** default true */ - preferParent: boolean, - /** default true */ - macDataPollKeepalive: boolean - /** default true */, - endDeviceKeepalive : boolean, - /** uint8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, -}; - -/** - * Holds radio parameters. - * - * This is mainly useful for dual PHY and switched radio device (2.4 GHz or SubGHz) to retrieve radio parameters. - */ -export type EmberMultiPhyRadioParameters = { - /** int8_t */ - radioTxPower: number, - /** uint8_t */ - radioPage: number, - /** uint8_t */ - radioChannel: number, -}; - -/** This structure contains information about child nodes. */ -export type EmberChildData = { - /** */ - eui64: EmberEUI64, - /** */ - type: EmberNodeType, - /** */ - id: EmberNodeId, - /** uint8_t */ - phy: number, - /** uint8_t */ - power: number, - /** uint8_t */ - timeout: number, - /** uint32_t */ - remainingTimeout: number, -}; - -/** - * Defines an entry in the neighbor table. - * - * A neighbor table entry stores information about the - * reliability of RF links to and from neighboring nodes. - */ -export type EmberNeighborTableEntry = { - /** The neighbor's two-byte network ID. uint16_t */ - shortId: number, - /** Filtered Link Quality indicator. uint8_t */ - averageLqi: number, - /** - * The incoming cost for this neighbor, computed from the average LQI. - * Values range from 1 for a good link to 7 for a bad link. - * uint8_t - */ - inCost: number, - /** The outgoing cost for this neighbor, obtained from the most recently - * received neighbor exchange message from the neighbor. A value of zero - * means that a neighbor exchange message from the neighbor has not been - * received recently enough, or that our ID was not present in the most - * recently received one. EmberZNet Pro only. - * uint8_t - */ - outCost: number, - /** In EmberZNet Pro, the number of aging periods elapsed since a neighbor - * exchange message was last received from this neighbor. In stack profile 1, - * the number of aging periods since any packet was received. - * An entry with an age greater than 6 is considered stale and may be - * reclaimed. In case the entry is used by a routing table entry it is - * considered stale with an age of 8. The aging period is 16 seconds. - * On receiving an incoming packet from the neighbor, the age is set to 3. - * uint8_t - * */ - age: number, - /** The 8 byte EUI64 of the neighbor. */ - longId: EmberEUI64, -}; - -/** - * Defines an entry in the route table. - * - * A route table entry stores information about the next - * hop along the route to the destination. - */ -export type EmberRouteTableEntry = { - /** The short ID of the destination. uint16_t */ - destination: number, - /** The short ID of the next hop to this destination. uint16_t */ - nextHop: number, - /** Indicates whether this entry is active (0), being discovered (1), or unused (3). uint8_t */ - status: number, - /** The number of seconds since this route entry was last used to send a packet. uint8_t */ - age: number, - /** Indicates whether this destination is a High-RAM Concentrator (2), a Low-RAM Concentrator (1), or not a concentrator (0). uint8_t */ - concentratorType: number, - /** - * For a High-RAM Concentrator, indicates whether a route record - * is needed (2), has been sent (1), or is no long needed (0) because - * a source routed message from the concentrator has been received. - * uint8_t - */ - routeRecordState: number, -}; - -/** - * A structure containing duty cycle limit configurations. - * - * All limits are absolute and are required to be as follows: suspLimit > critThresh > limitThresh - * For example: suspLimit = 250 (2.5%), critThresh = 180 (1.8%), limitThresh 100 (1.00%). - */ -export type EmberDutyCycleLimits = { - /** The Limited Threshold in % * 100. */ - limitThresh: EmberDutyCycleHectoPct, - /** The Critical Threshold in % * 100. */ - critThresh: EmberDutyCycleHectoPct, - /** The Suspended Limit (LBT) in % * 100. */ - suspLimit: EmberDutyCycleHectoPct, -}; - -/** 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, - /** The amount of overall duty cycle consumed (up to suspend limit). */ - dutyCycleConsumed: EmberDutyCycleHectoPct, -}; - -/** Defines a iterator used to loop over cached beacons. Fields denoted with a private comment should not be written to. */ -export type EmberBeaconIterator = { - /** Public fields */ - beacon: EmberBeaconData, - /** Private fields - Do not write to these variables. uint8_t */ - index: number, -}; - -/** - * Defines an entry in the binding table. - * - * A binding entry specifies a local endpoint, a remote endpoint, a - * cluster ID and either the destination EUI64 (for unicast bindings) or the - * 64-bit group address (for multicast bindings). - */ -export type EmberBindingTableEntry = { - /** The type of binding. */ - type: EmberBindingType , - /** The endpoint on the local node. uint8_t */ - local: number, - /** A cluster ID that matches one from the local endpoint's simple descriptor. - * This cluster ID is set by the provisioning application to indicate which - * part an endpoint's functionality is bound to this particular remote node - * and is used to distinguish between unicast and multicast bindings. Note - * that a binding can be used to to send messages with any cluster ID, not - * just that listed in the binding. - * uint16_t - */ - clusterId: number, - /** The endpoint on the remote node (specified by \c identifier). uint8_t */ - remote: number, - /** A 64-bit identifier. This is either: - * - The destination EUI64, for unicasts. - * - A 16-bit multicast group address, for multicasts. - */ - identifier: EmberEUI64, - /** The index of the network the binding belongs to. uint8_t */ - networkIndex: number, -}; - -/** An in-memory representation of a ZigBee APS frame of an incoming or outgoing message. */ -export type EmberApsFrame = { - /** The application profile ID that describes the format of the message. uint16_t */ - profileId: number, - /** The cluster ID for this message. uint16_t */ - clusterId: number, - /** The source endpoint. uint8_t */ - sourceEndpoint: number, - /** The destination endpoint. uint8_t */ - destinationEndpoint: number, - /** A bitmask of options from the enumeration above. */ - options: EmberApsOption, - /** The group ID for this message, if it is multicast mode. uint16_t */ - groupId: number, - /** The sequence number. uint8_t */ - sequence: number, - /** uint8_t */ - radius?: number,// XXX: marked optional since doesn't appear to be used -}; - -/** - * Defines an entry in the multicast table. - * - * A multicast table entry indicates that a particular endpoint is a member of a particular multicast group. - * Only devices with an endpoint in a multicast group will receive messages sent to that multicast group. - */ -export type EmberMulticastTableEntry = { - /** The multicast group ID. */ - multicastId: EmberMulticastId, - /** The endpoint that is a member, or 0 if this entry is not in use (the ZDO is not a member of any multicast groups). uint8_t */ - endpoint: number, - /** The network index of the network the entry is related to. uint8_t */ - networkIndex: number, -}; - -export type EmberBeaconClassificationParams = { - /** int8_t */ - minRssiForReceivingPkts: number, - /** uint16_t */ - beaconClassificationMask: number, -}; - -/** This data structure contains the key data that is passed into various other functions. */ -export type EmberKeyData = { - /** This is the key byte data. uint8_t[EMBER_ENCRYPTION_KEY_SIZE] */ - contents: Buffer; -}; - -/** This describes the Initial Security features and requirements that will be used when forming or joining the network. */ -export type EmberInitialSecurityState = { - /** - * This bitmask enumerates which security features should be used and the presence of valid data within other elements of the - * ::EmberInitialSecurityState data structure. For more details, see the ::EmberInitialSecurityBitmask. - * uint16_t - */ - bitmask: number, - /** - * This is the pre-configured key that can be used by devices when joining the network if the Trust Center does not send - * the initial security data in-the-clear. - * For the Trust Center, it will be the global link key and must be set regardless of whether joining devices are - * expected to have a pre-configured Link Key. This parameter will only be used if the EmberInitialSecurityState::bitmask - * sets the bit indicating ::EMBER_HAVE_PRECONFIGURED_KEY. - */ - preconfiguredKey: EmberKeyData, - /** - * This is the Network Key used when initially forming the network. - * It must be set on the Trust Center and is not needed for devices joining the network. - * This parameter will only be used if the EmberInitialSecurityState::bitmask sets the bit indicating ::EMBER_HAVE_NETWORK_KEY. - */ - networkKey: EmberKeyData, - /** - * This is the sequence number associated with the network key. It must be set if the Network Key is set and is used to indicate - * a particular of the network key for updating and switching. - * This parameter will only be used if the ::EMBER_HAVE_NETWORK_KEY is set. - * Generally, it should be set to 0 when forming the network; joining devices can ignore this value. - * uint8_t - * */ - networkKeySequenceNumber: number, - /** - * This is the long address of the trust center on the network that will be joined. - * It is usually NOT set prior to joining the network and is learned during the joining message exchange. - * This field is only examined if ::EMBER_HAVE_TRUST_CENTER_EUI64 is set in the EmberInitialSecurityState::bitmask. - * Most devices should clear that bit and leave this field alone. - * This field must be set when using commissioning mode. - * It is required to be in little-endian format. - */ - preconfiguredTrustCenterEui64: EmberEUI64, -}; - -/** This describes the security features used by the stack for a joined device. */ -export type EmberCurrentSecurityState = { - /** This bitmask indicates the security features currently in use on this node. */ - bitmask: EmberCurrentSecurityBitmask, - /** - * 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, -}; - -/** - * This data structure houses the context when interacting with the Zigbee - * Security Manager APIs. For example, when importing a key into storage, the various - * fields of this structure are used to determine which type of key is being stored. - * */ -export type SecManContext = { - coreKeyType: SecManKeyType, - /** uint8_t */ - keyIndex: number, - derivedType: SecManDerivedKeyType, - eui64: EmberEUI64, - /** uint8_t */ - multiNetworkIndex: number, - flags: SecManFlag, - /** - * Unused for classic key storage. - * The algorithm type should be brought in by psa/crypto_types.h. - * Zigbee Security Manager uses PSA_ALG_ECB_NO_PADDING for keys with AES-ECB encryption, - * and defines ZB_PSA_ALG as AES-CCM with a 4-byte tag, used as this field's default value otherwise. - * uint32_t - */ - psaKeyAlgPermission: number, -}; - -/** This data structure contains the metadata pertaining to an network key */ -export type SecManNetworkKeyInfo = { - networkKeySet: boolean, - alternateNetworkKeySet: boolean, - /** uint8_t */ - networkKeySequenceNumber: number, - /** uint8_t */ - altNetworkKeySequenceNumber: number, - /** uint32_t */ - networkKeyFrameCounter: number, -}; - -/** This data structure contains the metadata pertaining to an APS key */ -export type SecManAPSKeyMetadata = { - bitmask: EmberKeyStructBitmask, - /** valid only if bitmask & EMBER_KEY_HAS_OUTGOING_FRAME_COUNTER uint32_t */ - outgoingFrameCounter: number, - /** valid only if bitmask & EMBER_KEY_HAS_INCOMING_FRAME_COUNTER uint32_t */ - incomingFrameCounter: number, - /** valid only if core_key_type == SL_ZB_SEC_MAN_KEY_TYPE_TC_LINK_WITH_TIMEOUT uint16_t */ - ttlInSeconds: number,// -}; - -/** This data structure contains the key data that is passed into various other functions. */ -export type SecManKey = EmberKeyData; - -/** This data structure contains the context data when calculating an AES MMO hash (message digest). */ -export type EmberAesMmoHashContext = { - /** uint8_t[EMBER_AES_HASH_BLOCK_SIZE] */ - result: Buffer, - /** uint32_t */ - length: number, -}; - -/** This data structure contains the public key data that is used for Certificate Based Key Exchange (CBKE). */ -export type EmberPublicKeyData = { - /** uint8_t[EMBER_PUBLIC_KEY_SIZE] */ - contents: Buffer, -}; - -/** This data structure contains the certificate data that is used for Certificate Based Key Exchange (CBKE). */ -export type EmberCertificateData = { - /** uint8_t[EMBER_CERTIFICATE_SIZE] */ - contents: Buffer; -}; - -/** This data structure contains the Shared Message Authentication Code SMAC) data that is used for Certificate Based Key Exchange (CBKE). */ -export type EmberSmacData = { - /** uint8_t[EMBER_SMAC_SIZE] */ - contents: Buffer; -}; - -/** This data structure contains the public key data that is used for Certificate Based Key Exchange (CBKE) in SECT283k1 Elliptical Cryptography. */ -export type EmberPublicKey283k1Data = { - /** uint8_t[EMBER_PUBLIC_KEY_283K1_SIZE] */ - contents: Buffer, -}; - -/** This data structure contains the private key data that is used for Certificate Based Key Exchange (CBKE) in SECT283k1 Elliptical Cryptography. */ -export type EmberPrivateKey283k1Data = { - /** uint8_t[EMBER_PRIVATE_KEY_283K1_SIZE] */ - contents: Buffer, -}; - -/** This data structure contains the certificate data that is used for Certificate Based Key Exchange (CBKE) in SECT283k1 Elliptical Cryptography. */ -export type EmberCertificate283k1Data = { - /* This is the certificate byte data. uint8_t[EMBER_CERTIFICATE_283K1_SIZE] */ - contents: Buffer, -}; - -/** This data structure contains an AES-MMO Hash (the message digest). */ -export type EmberMessageDigest = { - /** uint8_t[EMBER_AES_HASH_BLOCK_SIZE] */ - contents: Buffer, -}; - -/** This data structure contains a DSA signature. It is the bit concatenation of the 'r' and 's' components of the signature. */ -export type EmberSignatureData = { - /** uint8_t[EMBER_SIGNATURE_SIZE] */ - contents: Buffer, -}; - -/** - * This data structure contains a DSA signature used in SECT283k1 Elliptical Cryptography. - * It is the bit concatenation of the 'r' and 's' components of the signature. - */ -export type EmberSignature283k1Data = { - /** uint8_t[EMBER_SIGNATURE_283K1_SIZE] */ - contents: Buffer; -}; - -/** This data structure contains the private key data that is used for Certificate Based Key Exchange (CBKE). */ -export type EmberPrivateKeyData = { - /** uint8_t[EMBER_PRIVATE_KEY_SIZE] */ - contents: Buffer, -}; - -/** Defines a ZigBee network and the associated parameters. */ -export type EmberZigbeeNetwork = { - /** uint16_t */ - panId: EmberPanId, - /** uint8_t */ - channel: number, - /** bool */ - allowingJoin: number, - /** uint8_t[EXTENDED_PAN_ID_SIZE] */ - extendedPanId: EmberExtendedPanId, - /** uint8_t */ - stackProfile: number, - /** uint8_t */ - nwkUpdateId: number, -}; - -/** Information about the ZLL security state and how to transmit the network key to the device securely. */ -export type EmberZllSecurityAlgorithmData = { - /** uint32_t */ - transactionId: number, - /** uint32_t */ - responseId: number, - /** uint16_t */ - bitmask: number, -}; - -/** Information about the ZLL network and specific device that responded to a ZLL scan request. */ -export type EmberZllNetwork = { - zigbeeNetwork: EmberZigbeeNetwork, - securityAlgorithm: EmberZllSecurityAlgorithmData, - eui64: EmberEUI64, - nodeId: EmberNodeId, - state: EmberZllState, - nodeType: EmberNodeType, - /** uint8_t */ - numberSubDevices: number, - /** uint8_t */ - totalGroupIdentifiers: number, - /** uint8_t */ - rssiCorrection: number, -}; - -/** Describe the Initial Security features and requirements that will be used when forming or joining ZigBee Light Link networks. */ -export type EmberZllInitialSecurityState = { - /** This bitmask is unused. All values are reserved for future use. uint32_t */ - bitmask: number, - /** The key encryption algorithm advertised by the application. */ - keyIndex: EmberZllKeyIndex, - /** The encryption key for use by algorithms that require it. */ - encryptionKey: EmberKeyData, - /** The pre-configured link key used during classical ZigBee commissioning. */ - preconfiguredKey: EmberKeyData, -}; - -/** Information discovered during a ZLL scan about the ZLL device's endpoint information. */ -export type EmberZllDeviceInfoRecord = { - ieeeAddress: EmberEUI64, - /** uint8_t */ - endpointId: number, - /** uint16_t */ - profileId: number, - /** uint16_t */ - deviceId: number, - /** uint8_t */ - version: number, - /** uint8_t */ - groupIdCount: number, -}; - -/** Network and group address assignment information. */ -export type EmberZllAddressAssignment = { - nodeId: EmberNodeId, - freeNodeIdMin: EmberNodeId, - freeNodeIdMax: EmberNodeId, - groupIdMin: EmberMulticastId, - groupIdMax: EmberMulticastId, - freeGroupIdMin: EmberMulticastId, - freeGroupIdMax: EmberMulticastId, -}; - -export type EmberTokTypeStackZllData = { - /** uint32_t */ - bitmask: number, - /** uint16_t */ - freeNodeIdMin: number, - /** uint16_t */ - freeNodeIdMax: number, - /** uint16_t */ - myGroupIdMin: number, - /** uint16_t */ - freeGroupIdMin: number, - /** uint16_t */ - freeGroupIdMax: number, - /** uint8_t */ - rssiCorrection: number, -}; - -export type EmberTokTypeStackZllSecurity = { - /** uint32_t */ - bitmask: number, - /** uint8_t */ - keyIndex: number, - /** uint8_t[EMBER_ENCRYPTION_KEY_SIZE] */ - encryptionKey: Buffer, - /** uint8_t[EMBER_ENCRYPTION_KEY_SIZE] */ - preconfiguredKey: Buffer, -}; - -/** 32-bit GPD source identifier uint32_t */ -export type EmberGpSourceId = number; - -/** - * GPD Address for sending and receiving a GPDF. - * EmberGpAddress_gpdIeeeAddress | EmberGpAddress_sourceId; - */ -export type EmberGpAddress = { - // union { - /** The IEEE address is used when the application identifier is ::EMBER_GP_APPLICATION_IEEE_ADDRESS. */ - gpdIeeeAddress?: EmberEUI64, - /** The 32-bit source identifier is used when the application identifier is ::EMBER_GP_APPLICATION_SOURCE_ID. */ - sourceId?: EmberGpSourceId, - // } id; - /** Application identifier of the GPD. */ - applicationId: EmberGpApplicationId, - /** Application endpoint , only used when application identifier is ::EMBER_GP_APPLICATION_IEEE_ADDRESS. uint8_t */ - endpoint: number, -}; - -/** 32-bit security frame counter uint32_t */ -export type EmberGpSecurityFrameCounter = number; - -/** The internal representation of a proxy table entry. */ -export type EmberGpProxyTableEntry = { - /** Internal status. Defines if the entry is unused or used as a proxy entry */ - status: EmberGpProxyTableEntryStatus, - /** The tunneling options (this contains both options and extendedOptions from the spec). uint32_t */ - options: number, - /** The addressing info of the GPD */ - gpd: EmberGpAddress, - /** The assigned alias for the GPD */ - assignedAlias: EmberNodeId, - /** The security options field. uint8_t */ - securityOptions: number, - /** The SFC of the GPD */ - gpdSecurityFrameCounter: EmberGpSecurityFrameCounter, - /** The key for the GPD. */ - gpdKey: EmberKeyData, - /** The list of sinks; hardcoded to 2, which is the spec minimum. EmberGpSinkListEntry[GP_SINK_LIST_ENTRIES] */ - sinkList: EmberGpSinkListEntry[], - /** The groupcast radius. uint8_t */ - groupcastRadius: number, - /** The search counter. uint8_t */ - searchCounter: number, -}; - -/** GP Sink Address. */ -export type EmberGpSinkAddress = { - /** EUI64 or long address of the sink */ - sinkEUI: EmberEUI64, - /** Node ID or network address of the sink */ - sinkNodeId: EmberNodeId, -}; - -/** GP Sink Group. */ -export type EmberGpSinkGroup = { - /** Group ID of the sink. uint16_t */ - groupID: number, - /** Alias ID of the sink. uint16_t */ - alias: number, -}; - -/** GP Sink List Entry. */ -export type EmberGpSinkListEntry = { - /** Sink Type */ - type: EmberGpSinkType, - // union { - unicast?: EmberGpSinkAddress, - groupcast?: EmberGpSinkGroup, - /** Entry for Sink Group List */ - groupList?: EmberGpSinkGroup, - // } target; -}; - - -/** The internal representation of a sink table entry. */ -export type EmberGpSinkTableEntry = { - /** Internal status. Defines if the entry is unused or used as a sink table entry */ - status: EmberGpSinkTableEntryStatus, - /** The tunneling options (this contains both options and extendedOptions from the spec). uint16_t */ - options: number, - /** The addressing info of the GPD */ - gpd: EmberGpAddress, - /** The device ID for the GPD. uint8_t */ - deviceId: number, - /** 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, - /** The groupcast radius. uint8_t */ - groupcastRadius: number, - /** The security options field. uint8_t */ - securityOptions: number, - /** The SFC of the GPD */ - gpdSecurityFrameCounter: EmberGpSecurityFrameCounter, - /** The GPD key associated with this entry. */ - gpdKey: EmberKeyData, -}; - -/** A structure containing the information of a token. */ -export type EmberTokenInfo = { - /** NVM3 token key. uint32_t */ - nvm3Key: number, - /** The token is a counter token type. */ - isCnt: boolean, - /** The token is an indexed token type. */ - isIdx: boolean, - /** Size of the object of the token. uint8_t */ - size: number, - /** The array size for the token when it is an indexed token. uint8_t */ - arraySize: number, -}; - -/** A structure containing the information of a token data. */ -export type EmberTokenData = { - /** The size of the token data in number of bytes. uint32_t */ - size: number, - /** A data pointer pointing to the storage for the token data of above size. void * */ - data: Buffer, -}; - -/** This data structure contains the transient key data that is used during Zigbee 3.0 joining. */ -export type EmberTransientKeyData = { - eui64: EmberEUI64, - /** uint32_t */ - incomingFrameCounter: number, - bitmask: EmberKeyStructBitmask, - /** uint16_t */ - remainingTimeSeconds: number, - /** uint8_t */ - networkIndex: number, - // union { - /** valid only if bitmask & EMBER_KEY_HAS_KEY_DATA (on some parts, keys are stored in secure storage and not RAM) */ - keyData?: EmberKeyData, - /** valid only if bitmask & EMBER_KEY_HAS_PSA_ID (on some parts, keys are stored in secure storage and not RAM). uint32_t */ - psa_id?: number, - // }, -}; diff --git a/src/adapter/ember/uart/ash.ts b/src/adapter/ember/uart/ash.ts deleted file mode 100644 index 46194a8329a..00000000000 --- a/src/adapter/ember/uart/ash.ts +++ /dev/null @@ -1,1882 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import {EventEmitter} from "stream"; -import {Socket} from "net"; -import SocketPortUtils from "../../socketPortUtils"; -import {SerialPort} from "../../serialPort"; -import {SerialPortOptions} from "../../tstype"; -import { - ASH_ACKNUM_BIT, - ASH_ACKNUM_MASK, - ASH_CRC_LEN, - ASH_DFRAME_MASK, - ASH_FLIP, - ASH_FRAME_LEN_ACK, - ASH_FRAME_LEN_DATA_MIN, - ASH_FRAME_LEN_ERROR, - ASH_FRAME_LEN_NAK, - ASH_FRAME_LEN_RSTACK, - ASH_FRMNUM_BIT, - ASH_FRMNUM_MASK, - ASH_MAX_DATA_FIELD_LEN, - ASH_MAX_FRAME_WITH_CRC_LEN, - ASH_MAX_TIMEOUTS, - ASH_MIN_DATA_FIELD_LEN, - ASH_MIN_FRAME_WITH_CRC_LEN, - ASH_NFLAG_BIT, - ASH_NFLAG_MASK, - ASH_RFLAG_BIT, - ASH_RFLAG_MASK, - ASH_SHFRAME_MASK, - ASH_VERSION, - ASH_WAKE, - EZSP_HOST_RX_POOL_SIZE, - LFSR_POLY, - LFSR_SEED, - SH_RX_BUFFER_LEN, - SH_TX_BUFFER_LEN, - TX_POOL_BUFFERS, -} from "./consts"; -import {inc8, mod8, withinRange, halCommonCrc16} from "../utils/math"; -import {EzspStatus} from "../enums"; -import {AshFrameType, AshReservedByte, NcpFailedCode} from "./enums"; -import {EzspBuffer, EzspFreeList, EzspQueue} from "./queues"; -import {AshWriter} from "./writer"; -import {AshParser} from "./parser"; -import {Wait} from "../../../utils"; - -const debug = Debug('zigbee-herdsman:adapter:ember:uart:ash'); - - -/** ASH get rflag in control byte */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const ashGetRFlag = (ctrl: number): number => ((ctrl & ASH_RFLAG_MASK) >> ASH_RFLAG_BIT); -/** ASH get nflag in control byte */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const ashGetNFlag = (ctrl: number): number => ((ctrl & ASH_NFLAG_MASK) >> ASH_NFLAG_BIT); -/** ASH get frmnum in control byte */ -const ashGetFrmNum = (ctrl: number): number => ((ctrl & ASH_FRMNUM_MASK) >> ASH_FRMNUM_BIT); -/** ASH get acknum in control byte */ -const ashGetACKNum = (ctrl: number): number => ((ctrl & ASH_ACKNUM_MASK) >> ASH_ACKNUM_BIT); - - -export enum AshEvents { - /** When the host detects a fatal error */ - hostError = 'hostError', - /** When the NCP reports a fatal error */ - ncpError = 'ncpError', - /** When a frame has been parsed and queued in the rxQueue. */ - frame = 'frame', -} - -type UartAshCounters = { - /** DATA frame data fields bytes transmitted */ - txData: number, - /** frames of all types transmitted */ - txAllFrames: number, - /** DATA frames transmitted */ - txDataFrames: number, - /** ACK frames transmitted */ - txAckFrames: number, - /** NAK frames transmitted */ - txNakFrames: number, - /** DATA frames retransmitted */ - txReDataFrames: number, - /** ACK and NAK frames with nFlag 0 transmitted */ - // txN0Frames: number, - /** ACK and NAK frames with nFlag 1 transmitted */ - txN1Frames: number, - /** frames cancelled (with ASH_CAN byte) */ - txCancelled: number, - - /** DATA frame data fields bytes received */ - rxData: number, - /** frames of all types received */ - rxAllFrames: number, - /** DATA frames received */ - rxDataFrames: number, - /** ACK frames received */ - rxAckFrames: number, - /** NAK frames received */ - rxNakFrames: number, - /** retransmitted DATA frames received */ - rxReDataFrames: number, - /** ACK and NAK frames with nFlag 0 received */ - // rxN0Frames: number, - /** ACK and NAK frames with nFlag 1 received */ - rxN1Frames: number, - /** frames cancelled (with ASH_CAN byte) */ - rxCancelled: number, - - /** frames with CRC errors */ - rxCrcErrors: number, - /** frames with comm errors (with ASH_SUB byte) */ - rxCommErrors: number, - /** frames shorter than minimum */ - rxTooShort: number, - /** frames longer than maximum */ - rxTooLong: number, - /** frames with illegal control byte */ - rxBadControl: number, - /** frames with illegal length for type of frame */ - rxBadLength: number, - /** frames with bad ACK numbers */ - rxBadAckNumber: number, - /** DATA frames discarded due to lack of buffers */ - rxNoBuffer: number, - /** duplicate retransmitted DATA frames */ - rxDuplicates: number, - /** DATA frames received out of sequence */ - rxOutOfSequence: number, - /** received ACK timeouts */ - rxAckTimeouts: number, -}; - -enum SendState { - IDLE = 0, - SHFRAME = 1, - TX_DATA = 2, - RETX_DATA = 3, -} - -// Bits in ashFlags -enum Flag { - /** Reject Condition */ - REJ = 0x01, - /** Retransmit Condition */ - RETX = 0x02, - /** send NAK */ - NAK = 0x04, - /** send ACK */ - ACK = 0x08, - /** send RST */ - RST = 0x10, - /** send immediate CAN */ - CAN = 0x20, - /** in CONNECTED state, else ERROR */ - CONNECTED = 0x40, - /** not ready to receive DATA frames */ - NR = 0x100, - /** last transmitted NR status */ - NRTX = 0x200, -} - - -/** max frames sent without being ACKed (1-7) */ -const CONFIG_TX_K = 3; -/** enables randomizing DATA frame payloads */ -const CONFIG_RANDOMIZE = true; -/** adaptive rec'd ACK timeout initial value */ -const CONFIG_ACK_TIME_INIT = 800; -/** " " " " " minimum value */ -const CONFIG_ACK_TIME_MIN = 400; -/** " " " " " maximum value */ -const CONFIG_ACK_TIME_MAX = 2400; -/** time allowed to receive RSTACK after ncp is reset */ -const CONFIG_TIME_RST = 2500; -/** time between checks for received RSTACK (CONNECTED status) */ -const CONFIG_TIME_RST_CHECK = 100; -/** if free buffers < limit, host receiver isn't ready, will hold off the ncp from sending normal priority frames */ -const CONFIG_NR_LOW_LIMIT = 8;// RX_FREE_LW -/** if free buffers > limit, host receiver is ready */ -const CONFIG_NR_HIGH_LIMIT = 12;// RX_FREE_HW -/** time until a set nFlag must be resent (max 2032) */ -const CONFIG_NR_TIME = 480; -/** Read/write max bytes count at stream level */ -const CONFIG_HIGHWATER_MARK = 256; - -/** - * ASH Protocol handler. - */ -export class UartAsh extends EventEmitter { - private readonly portOptions: SerialPortOptions; - private serialPort: SerialPort; - private socketPort: Socket; - private writer: AshWriter; - private parser: AshParser; - - /** True when serial/socket is currently closing. */ - private closing: boolean; - - /** time ackTimer started: 0 means not ready uint16_t */ - private ackTimer: number; - /** time used to check ackTimer expiry (msecs) uint16_t */ - private ackPeriod: number; - /** not ready timer (16 msec units). Set to (now + config.nrTime) when started. uint8_t */ - private nrTimer: number; - /** frame decode in progress */ - private decodeInProgress: boolean; - - // Variables used in encoding frames - /** true when preceding byte was escaped */ - private encodeEscFlag: boolean; - /** byte to send after ASH_ESC uint8_t */ - private encodeFlip: number; - /** uint16_t */ - private encodeCrc: number; - /** encoder state: 0 = control/data bytes, 1 = crc low byte, 2 = crc high byte, 3 = flag. uint8_t */ - private encodeState: number; - /** bytes remaining to encode. uint8_t */ - private encodeCount: number; - - // Variables used in decoding frames - /** bytes in frame, plus CRC, clamped to limit +1: high values also used to record certain errors. uint8_t */ - private decodeLen: number; - /** ASH_FLIP if previous byte was ASH_ESC. uint8_t */ - private decodeFlip: number; - /** a 2 byte queue to avoid outputting crc bytes. uint8_t */ - private decodeByte1: number; - /** at frame end, they contain the received crc. uint8_t */ - private decodeByte2: number; - /** uint16_t */ - private decodeCrc: number; - - /** outgoing short frames */ - private txSHBuffer: Buffer; - /** incoming short frames */ - private rxSHBuffer: Buffer; - - /** bit flags for top-level logic. uint16_t */ - private flags: number; - /** frame ack'ed from remote peer. uint8_t */ - private ackRx: number; - /** frame ack'ed to remote peer. uint8_t */ - private ackTx: number; - /** next frame to be transmitted. uint8_t */ - private frmTx: number; - /** next frame to be retransmitted. uint8_t */ - private frmReTx: number; - /** next frame expected to be rec'd. uint8_t */ - private frmRx: number; - /** frame at retx queue's head. uint8_t */ - private frmReTxHead: number; - /** consecutive timeout counter. uint8_t */ - private timeouts: number; - /** rec'd DATA frame buffer. uint8_t */ - private rxDataBuffer: EzspBuffer; - /** rec'd frame length. uint8_t */ - private rxLen: number; - /** tx frame offset. uint8_t */ - private txOffset: number; - - public counters: UartAshCounters; - - private ncpError: EzspStatus; - private hostError: EzspStatus; - /** sendExec() state variable */ - private sendState: SendState; - - /** NCP is enabled to sleep, set by EZSP, not supported atm, always false */ - public ncpSleepEnabled: boolean; - /** - * Set when the ncp has indicated it has a pending callback by seting the callback flag in the frame control byte - * or (uart version only) by sending an an ASH_WAKE byte between frames. - */ - public ncpHasCallbacks: boolean; - - /** Transmit buffers */ - private txPool: EzspBuffer[]; - public txQueue: EzspQueue; - public reTxQueue: EzspQueue; - public txFree: EzspFreeList; - - /** Receive buffers */ - private rxPool: EzspBuffer[]; - public rxQueue: EzspQueue; - public rxFree: EzspFreeList; - - constructor(options: SerialPortOptions) { - super(); - - this.portOptions = options; - this.serialPort = null; - this.socketPort = null; - this.writer = null; - this.parser = null; - - this.txPool = new Array(TX_POOL_BUFFERS); - this.txQueue = new EzspQueue(); - this.reTxQueue = new EzspQueue(); - this.txFree = new EzspFreeList(); - - this.rxPool = new Array(EZSP_HOST_RX_POOL_SIZE); - this.rxQueue = new EzspQueue(); - this.rxFree = new EzspFreeList(); - } - - /** - * Check if port is valid, open, and not closing. - */ - get portOpen(): boolean { - if (this.closing) { - return false; - } - - return this.serialPort != null ? this.serialPort.isOpen : !this.socketPort?.closed; - } - - /** - * Get max wait time before response is considered timed out. - */ - get responseTimeout(): number { - return ASH_MAX_TIMEOUTS * CONFIG_ACK_TIME_MAX; - } - - /** - * Indicates if the host is in the Connected state. - * If not, the host and NCP cannot exchange DATA frames. - * Note that this function does not actively confirm that communication with NCP is healthy, but simply returns its last known status. - * - * @returns - * - true - host and NCP can exchange DATA frames - * - false - host and NCP cannot now exchange DATA frames - */ - get connected(): boolean { - return ((this.flags & Flag.CONNECTED) !== 0); - } - - /** - * Has nothing to do... - */ - get idle(): boolean { - return ( - !this.decodeInProgress // don't have a partial frame - // && (this.serial.readAvailable() === EzspStatus.NO_RX_DATA) // no rx data - && this.rxQueue.empty // no rx frames to process - && !this.ncpHasCallbacks // no pending callbacks - && (this.flags === Flag.CONNECTED) // no pending ACKs, NAKs, etc. - && (this.ackTx === this.frmRx) // do not need to send an ACK - && (this.ackRx === this.frmTx) // not waiting to receive an ACK - && (this.sendState === SendState.IDLE) // nothing being transmitted now - && this.txQueue.empty // nothing waiting to transmit - // && this.serial.outputIsIdle() // nothing in OS buffers or UART FIFO - ); - } - - /** - * Initialize ASH variables, timers and queues, but not the serial port - */ - private initVariables(): void { - this.closing = false; - - this.serialPort = null; - this.socketPort = null; - this.writer = null; - this.parser = null; - this.txSHBuffer = Buffer.alloc(SH_TX_BUFFER_LEN); - this.rxSHBuffer = Buffer.alloc(SH_RX_BUFFER_LEN); - this.ackTimer = 0; - this.ackPeriod = 0; - this.nrTimer = 0; - - this.flags = 0; - this.decodeInProgress = false; - this.ackRx = 0; - this.ackTx = 0; - this.frmTx = 0; - this.frmReTx = 0; - this.frmRx = 0; - this.frmReTxHead = 0; - this.timeouts = 0; - this.rxDataBuffer = null; - this.rxLen = 0; - - // init to "start of frame" default - this.encodeCount = 0; - this.encodeState = 0; - this.encodeEscFlag = false; - this.encodeCrc = 0xFFFF; - this.txOffset = 0; - - // init to "start of frame" default - this.decodeLen = 0; - this.decodeByte1 = 0; - this.decodeByte2 = 0; - this.decodeFlip = 0; - this.decodeCrc = 0xFFFF; - - this.ncpError = EzspStatus.NO_ERROR; - this.hostError = EzspStatus.NO_ERROR; - this.sendState = SendState.IDLE; - - this.ncpSleepEnabled = false; - this.ncpHasCallbacks = false; - - this.stopAckTimer(); - this.stopNrTimer(); - this.initQueues(); - - this.counters = { - txData: 0, - txAllFrames: 0, - txDataFrames: 0, - txAckFrames: 0, - txNakFrames: 0, - txReDataFrames: 0, - // txN0Frames: 0, - txN1Frames: 0, - txCancelled: 0, - - rxData: 0, - rxAllFrames: 0, - rxDataFrames: 0, - rxAckFrames: 0, - rxNakFrames: 0, - rxReDataFrames: 0, - // rxN0Frames: 0, - rxN1Frames: 0, - rxCancelled: 0, - - rxCrcErrors: 0, - rxCommErrors: 0, - rxTooShort: 0, - rxTooLong: 0, - rxBadControl: 0, - rxBadLength: 0, - rxBadAckNumber: 0, - rxNoBuffer: 0, - rxDuplicates: 0, - rxOutOfSequence: 0, - rxAckTimeouts: 0, - }; - } - - /** - * Initializes all queues and free lists. - * All receive buffers are put into rxFree, and rxQueue is empty. - * All transmit buffers are put into txFree, and txQueue and reTxQueue are empty. - */ - private initQueues(): void { - this.txQueue.tail = null; - this.reTxQueue.tail = null; - this.txFree.link = null; - - for (let i = 0; i < TX_POOL_BUFFERS; i++) { - this.txFree.freeBuffer(this.txPool[i] = new EzspBuffer()); - } - - this.rxQueue.tail = null; - this.rxFree.link = null; - - for (let i = 0; i < EZSP_HOST_RX_POOL_SIZE; i++) { - this.rxFree.freeBuffer(this.rxPool[i] = new EzspBuffer()); - } - } - - private async initPort(): Promise { - if (!SocketPortUtils.isTcpPath(this.portOptions.path)) { - if (this.serialPort != null) { - this.serialPort.close(); - } - - const serialOpts = { - path: this.portOptions.path, - baudRate: typeof this.portOptions.baudRate === 'number' ? this.portOptions.baudRate : 115200, - rtscts: typeof this.portOptions.rtscts === 'boolean' ? this.portOptions.rtscts : false, - autoOpen: false, - parity: 'none' as const, - stopBits: 1 as const, - xon: false, - xoff: false, - }; - - // enable software flow control if RTS/CTS not enabled in config - if (!serialOpts.rtscts) { - debug(`RTS/CTS config is off, enabling software flow control.`); - serialOpts.xon = true; - serialOpts.xoff = true; - } - - //@ts-expect-error Jest testing - if (this.portOptions.binding != null) { - //@ts-expect-error Jest testing - serialOpts.binding = this.portOptions.binding; - } - - debug(`Opening SerialPort with ${JSON.stringify(serialOpts)}`); - this.serialPort = new SerialPort(serialOpts); - - this.writer = new AshWriter({highWaterMark: CONFIG_HIGHWATER_MARK}); - this.writer.pipe(this.serialPort); - - this.parser = new AshParser({readableHighWaterMark: CONFIG_HIGHWATER_MARK}); - this.serialPort.pipe(this.parser); - this.parser.on('data', this.onFrame.bind(this)); - - try { - await this.serialPort.asyncOpen(); - debug('Serialport opened'); - - this.serialPort.once('close', this.onPortClose.bind(this)); - this.serialPort.on('error', this.onPortError.bind(this)); - } catch (error) { - await this.stop(); - - throw error; - } - } else { - if (this.socketPort != null) { - this.socketPort.destroy(); - } - - const info = SocketPortUtils.parseTcpPath(this.portOptions.path); - debug(`Opening TCP socket with ${info.host}:${info.port}`); - - this.socketPort = new Socket(); - this.socketPort.setNoDelay(true); - this.socketPort.setKeepAlive(true, 15000); - - this.writer = new AshWriter({highWaterMark: CONFIG_HIGHWATER_MARK}); - this.writer.pipe(this.socketPort); - - this.parser = new AshParser({readableHighWaterMark: CONFIG_HIGHWATER_MARK}); - this.socketPort.pipe(this.parser); - this.parser.on('data', this.onFrame.bind(this)); - - return new Promise((resolve, reject): void => { - const openError = async (err: Error): Promise => { - await this.stop(); - - reject(err); - }; - - this.socketPort.on('connect', () => { - debug('Socket connected'); - }); - this.socketPort.on('ready', async (): Promise => { - debug('Socket ready'); - this.socketPort.removeListener('error', openError); - this.socketPort.once('close', this.onPortClose.bind(this)); - this.socketPort.on('error', this.onPortError.bind(this)); - - resolve(); - }); - this.socketPort.once('error', openError); - - this.socketPort.connect(info.port, info.host); - }); - } - } - - /** - * Handle port closing - * @param err A boolean for Socket, an Error for serialport - */ - private async onPortClose(err: boolean | Error): Promise { - console.log(`Port closed. Error? ${err ?? 'no'}`); - } - - /** - * Handle port error - * @param error - */ - private async onPortError(error: Error): Promise { - console.log(`Port error: ${error}`); - this.hostDisconnect(EzspStatus.ERROR_SERIAL_INIT); - await this.stop(); - } - - /** - * Handle received frame from AshParser. - * @param buf - */ - private onFrame(buffer: Buffer): void { - const iCAN = buffer.lastIndexOf(AshReservedByte.CANCEL);// should only be one, but just in case... - - if (iCAN !== -1) { - // ignore the cancel before RSTACK - if (this.flags & Flag.CONNECTED) { - this.counters.rxCancelled += 1; - - console.warn(`Frame(s) in progress cancelled. ${buffer.subarray(0, iCAN).toString('hex')}`); - } - - // get rid of everything up to the CAN flag and start reading frame from there, no need to loop through bytes in vain - buffer = buffer.subarray(iCAN + 1); - } - - const status = this.receiveFrame(buffer); - - if (status === EzspStatus.SUCCESS) { - // ? - } else if ((status !== EzspStatus.ASH_IN_PROGRESS) && (status !== EzspStatus.NO_RX_DATA)) { - throw new Error(EzspStatus[status]); - } - } - - /** - * Initializes the ASH protocol, and waits until the NCP finishes rebooting, or a non-recoverable error occurs. - * - * @returns - * - EzspStatus.SUCCESS - * - EzspStatus.HOST_FATAL_ERROR - * - EzspStatus.ASH_NCP_FATAL_ERROR) - */ - public async start(): Promise { - if (this.closing || (this.flags & Flag.CONNECTED)) { - return EzspStatus.ERROR_INVALID_CALL; - } - - console.log(`======== ASH starting ========`); - - if (this.serialPort != null) { - this.serialPort.flush();// clear read/write buffers - } else { - // XXX: Socket equiv? - } - - this.sendExec(); - - // block til RSTACK or timeout - for (let i = 0; i < CONFIG_TIME_RST; i += CONFIG_TIME_RST_CHECK) { - if ((this.flags & Flag.CONNECTED)) { - console.log(`======== ASH started ========`); - - return EzspStatus.SUCCESS; - } - - debug(`Waiting for RSTACK... ${i}/${CONFIG_TIME_RST}`); - await Wait(CONFIG_TIME_RST_CHECK); - } - - return EzspStatus.HOST_FATAL_ERROR; - } - - /** - * Stops the ASH protocol - flushes and closes the serial port, clears all queues, stops timers, etc. - */ - public async stop(): Promise { - this.closing = true; - - this.printCounters(); - - if (this.serialPort?.isOpen) { - try { - await this.serialPort.asyncFlushAndClose(); - - debug(`Serial port closed.`); - } catch (err) { - debug(`Failed to close serial port ${err}.`); - } - - this.serialPort.removeAllListeners(); - } else if (this.socketPort != null && !this.socketPort.closed) { - this.socketPort.destroy(); - this.socketPort.removeAllListeners(); - - debug(`Socket port closed.`); - } - - this.initVariables(); - console.log(`======== ASH stopped ========`); - } - - /** - * Initializes the ASH serial port and (if enabled) resets the NCP. - * The method used to do the reset is specified by the the host configuration parameter resetMethod. - * - * When the reset method is sending a RST frame, the caller should retry NCP resets a few times if it fails. - * - * @returns - * - EzspStatus.SUCCESS - * - EzspStatus.HOST_FATAL_ERROR - */ - public async resetNcp(): Promise { - if (this.closing) { - return EzspStatus.ERROR_INVALID_CALL; - } - - console.log(`======== ASH NCP reset ========`); - - this.initVariables(); - - let status: EzspStatus; - - // ask ncp to reset itself using RST frame - try { - await this.initPort(); - - this.flags = Flag.RST | Flag.CAN; - - return EzspStatus.SUCCESS; - } catch (err) { - this.hostError = status; - - return EzspStatus.HOST_FATAL_ERROR; - } - } - - /** - * Adds a DATA frame to the transmit queue to send to the NCP. - * Frames that are too long or too short will not be sent, and frames will not be added to the queue - * if the host is not in the Connected state, or the NCP is not ready to receive a DATA frame or if there - * is no room in the queue; - * - * @param len length of data field - * @param inBuf array containing the data to be sent - * - * @returns - * - EzspStatus.SUCCESS - * - EzspStatus.NO_TX_SPACE - * - EzspStatus.DATA_FRAME_TOO_SHORT - * - EzspStatus.DATA_FRAME_TOO_LONG - * - EzspStatus.NOT_CONNECTED - */ - public send(len: number, inBuf: Buffer): EzspStatus { - // Check for errors that might have been detected - if (this.hostError !== EzspStatus.NO_ERROR) { - return EzspStatus.HOST_FATAL_ERROR; - } - - if (this.ncpError !== EzspStatus.NO_ERROR) { - return EzspStatus.ASH_NCP_FATAL_ERROR; - } - - // After verifying that the data field length is within bounds, - // copies data frame to a buffer and appends it to the transmit queue. - if (len < ASH_MIN_DATA_FIELD_LEN) { - return EzspStatus.DATA_FRAME_TOO_SHORT; - } else if (len > ASH_MAX_DATA_FIELD_LEN) { - return EzspStatus.DATA_FRAME_TOO_LONG; - } - - if (!(this.flags & Flag.CONNECTED)) { - return EzspStatus.NOT_CONNECTED; - } - - const buffer: EzspBuffer = this.txFree.allocBuffer(); - - if (buffer == null) { - return EzspStatus.NO_TX_SPACE; - } - - inBuf.copy(buffer.data, 0, 0, len); - - buffer.len = len; - - this.randomizeBuffer(buffer.data, buffer.len);// IN/OUT data - this.txQueue.addTail(buffer); - this.sendExec(); - - return EzspStatus.SUCCESS; - } - - /** - * Manages outgoing communication to the NCP, including DATA frames as well as the frames used for - * initialization and error detection and recovery. - */ - public sendExec(): void { - let outByte: number = 0x00; - let inByte: number = 0x00; - let len: number = 0; - let buffer: EzspBuffer = null; - - // Check for received acknowledgement timer expiry - if (this.ackTimerHasExpired()) { - if (this.flags & Flag.CONNECTED) { - const expectedFrm = ((this.flags & Flag.RETX) ? this.frmReTx : this.frmTx); - - if (this.ackRx !== expectedFrm) { - this.counters.rxAckTimeouts += 1; - - this.adjustAckPeriod(true); - - debug(`Timer expired waiting for ACK for ${expectedFrm}, current=${this.ackRx}`); - - if (++this.timeouts >= ASH_MAX_TIMEOUTS) { - this.hostDisconnect(EzspStatus.ASH_ERROR_TIMEOUTS); - - return; - } - - this.startRetransmission(); - } else { - this.stopAckTimer(); - } - } else { - this.hostDisconnect(EzspStatus.ASH_ERROR_RESET_FAIL); - } - } - - while (this.writer.writeAvailable()) { - // Send ASH_CAN character immediately, ahead of any other transmit data - if (this.flags & Flag.CAN) { - if (this.sendState === SendState.IDLE) { - // sending RST or just woke NCP - this.writer.writeByte(AshReservedByte.CANCEL); - } else if (this.sendState === SendState.TX_DATA) { - // cancel frame in progress - this.counters.txCancelled += 1; - - this.writer.writeByte(AshReservedByte.CANCEL); - - this.stopAckTimer(); - - this.sendState = SendState.IDLE; - } - - this.flags &= ~Flag.CAN; - - continue; - } - - switch (this.sendState) { - case SendState.IDLE: - // In between frames - do some housekeeping and decide what to send next - // If retransmitting, set the next frame to send to the last ackNum - // received, then check to see if retransmission is now complete. - if (this.flags & Flag.RETX) { - if (withinRange(this.frmReTx, this.ackRx, this.frmTx)) { - this.frmReTx = this.ackRx; - } - - if (this.frmReTx === this.frmTx) { - this.flags &= ~Flag.RETX; - - this.scrubReTxQueue(); - } - } - - // restrain ncp if needed - this.dataFrameFlowControl(); - - // See if a short frame is flagged to be sent - // The order of the tests below - RST, NAK and ACK - - // sets the relative priority of sending these frame types. - if (this.flags & Flag.RST) { - this.txSHBuffer[0] = AshFrameType.RST; - - this.setAndStartAckTimer(CONFIG_TIME_RST); - - len = 1; - this.flags &= ~(Flag.RST | Flag.NAK | Flag.ACK); - this.sendState = SendState.SHFRAME; - debug(`---> [FRAME type=RST]`); - } else if (this.flags & (Flag.NAK | Flag.ACK)) { - if (this.flags & Flag.NAK) { - this.txSHBuffer[0] = AshFrameType.NAK + (this.frmRx << ASH_ACKNUM_BIT); - this.flags &= ~(Flag.NRTX | Flag.NAK | Flag.ACK); - debug(`---> [FRAME type=NAK frmRx=${this.frmRx}]`); - } else { - this.txSHBuffer[0] = AshFrameType.ACK + (this.frmRx << ASH_ACKNUM_BIT); - this.flags &= ~(Flag.NRTX | Flag.ACK); - debug(`---> [FRAME type=ACK frmRx=${this.frmRx}]`); - } - - if (this.flags & Flag.NR) { - this.txSHBuffer[0] |= ASH_NFLAG_MASK; - this.flags |= Flag.NRTX; - - this.startNrTimer(); - } - - this.ackTx = this.frmRx; - len = 1; - this.sendState = SendState.SHFRAME; - } else if (this.flags & Flag.RETX) { - // Retransmitting DATA frames for error recovery - buffer = this.reTxQueue.getNthEntry(mod8(this.frmTx - this.frmReTx)); - len = buffer.len + 1; - this.txSHBuffer[0] = AshFrameType.DATA | (this.frmReTx << ASH_FRMNUM_BIT) | (this.frmRx << ASH_ACKNUM_BIT) | ASH_RFLAG_MASK; - this.sendState = SendState.RETX_DATA; - debug(`---> [FRAME type=DATA_RETX frmReTx=${this.frmReTx} frmRx=${this.frmRx}]`); - } else if (this.ackTx != this.frmRx) { - // An ACK should be generated - this.flags |= Flag.ACK; - break; - } else if (!this.txQueue.empty && withinRange(this.ackRx, this.frmTx, (this.ackRx + CONFIG_TX_K - 1)) ) { - // Send a DATA frame if ready - buffer = this.txQueue.head; - len = buffer.len + 1; - - this.counters.txData += (len - 1); - - this.txSHBuffer[0] = AshFrameType.DATA | (this.frmTx << ASH_FRMNUM_BIT) | (this.frmRx << ASH_ACKNUM_BIT); - this.sendState = SendState.TX_DATA; - debug(`---> [FRAME type=DATA frmTx=${this.frmTx} frmRx=${this.frmRx}]`); - } else { - // Otherwise there's nothing to send - this.writer.writeFlush(); - - return; - } - - this.countFrame(true); - - // Start frame - encodeByte() is inited by a non-zero length argument - outByte = this.encodeByte(len, this.txSHBuffer[0]); - - this.writer.writeByte(outByte); - break; - case SendState.SHFRAME: - // sending short frame - if (this.txOffset !== 0xFF) { - inByte = this.txSHBuffer[this.txOffset]; - outByte = this.encodeByte(0, inByte); - - this.writer.writeByte(outByte); - } else { - this.sendState = SendState.IDLE; - } - break; - case SendState.TX_DATA: - case SendState.RETX_DATA: - // sending OR resending data frame - if (this.txOffset !== 0xFF) { - inByte = this.txOffset ? buffer.data[this.txOffset - 1] : this.txSHBuffer[0]; - outByte = this.encodeByte(0, inByte); - - this.writer.writeByte(outByte); - } else { - if (this.sendState === SendState.TX_DATA) { - this.frmTx = inc8(this.frmTx); - buffer = this.txQueue.removeHead(); - - this.reTxQueue.addTail(buffer); - } else { - this.frmReTx = inc8(this.frmReTx); - } - - if (this.ackTimerIsNotRunning()) { - this.startAckTimer(); - } - - this.ackTx = this.frmRx; - this.sendState = SendState.IDLE; - } - break; - } - } - - this.writer.writeFlush(); - } - - /** - * Retrieve a frame and accept, reTx, reject, fail based on type & validity in current state. - * @returns - * - EzspStatus.SUCCESS On valid RSTACK or valid DATA frame. - * - EzspStatus.ASH_IN_PROGRESS - * - EzspStatus.NO_RX_DATA - * - EzspStatus.NO_RX_SPACE - * - EzspStatus.HOST_FATAL_ERROR - * - EzspStatus.ASH_NCP_FATAL_ERROR - */ - private receiveFrame(buffer: Buffer): EzspStatus { - // Check for errors that might have been detected - if (this.hostError !== EzspStatus.NO_ERROR) { - return EzspStatus.HOST_FATAL_ERROR; - } - - if (this.ncpError !== EzspStatus.NO_ERROR) { - return EzspStatus.ASH_NCP_FATAL_ERROR; - } - - let ackNum: number = 0; - let frmNum: number = 0; - let frameType: AshFrameType = AshFrameType.INVALID; - - // Read data from serial port and assemble a frame until complete, aborted - // due to an error, cancelled, or there is no more serial data available. - const status = this.readFrame(buffer); - - switch (status) { - case EzspStatus.SUCCESS: - break; - case EzspStatus.ASH_IN_PROGRESS: - // should have a complete frame by now, if not, don't process further - return EzspStatus.NO_RX_DATA; - case EzspStatus.ASH_CANCELLED: - // should have been taken out in onFrame - return this.hostDisconnect(status); - case EzspStatus.ASH_BAD_CRC: - this.counters.rxCrcErrors += 1; - - this.rejectFrame(); - console.error(`Received frame with CRC error`); - return EzspStatus.NO_RX_DATA; - case EzspStatus.ASH_COMM_ERROR: - this.counters.rxCommErrors += 1; - - this.rejectFrame(); - console.error(`Received frame with comm error`); - return EzspStatus.NO_RX_DATA; - case EzspStatus.ASH_TOO_SHORT: - this.counters.rxTooShort += 1; - - this.rejectFrame(); - console.error(`Received frame shorter than minimum`); - return EzspStatus.NO_RX_DATA; - case EzspStatus.ASH_TOO_LONG: - this.counters.rxTooLong += 1; - - this.rejectFrame(); - console.error(`Received frame longer than maximum`); - return EzspStatus.NO_RX_DATA; - case EzspStatus.ASH_ERROR_XON_XOFF: - return this.hostDisconnect(status); - default: - throw new Error(`Unhandled error while receiving frame.`); - } - - // Got a complete frame - validate its control and length. - // On an error the type returned will be TYPE_INVALID. - frameType = this.getFrameType(this.rxSHBuffer[0], this.rxLen); - - // Free buffer allocated for a received frame if: - // DATA frame, and out of order - // DATA frame, and not in the CONNECTED state - // not a DATA frame - if (frameType === AshFrameType.DATA) { - if (!(this.flags & Flag.CONNECTED) || (ashGetFrmNum(this.rxSHBuffer[0]) !== this.frmRx)) { - this.freeNonNullRxBuffer(); - } - } else { - this.freeNonNullRxBuffer(); - } - - const frameTypeStr = AshFrameType[frameType]; - - debug(`<--- [FRAME type=${frameTypeStr}]`); - this.countFrame(false); - - // Process frames received while not in the connected state - - // ignore everything except RSTACK and ERROR frames - if (!(this.flags & Flag.CONNECTED)) { - if (frameType === AshFrameType.RSTACK) { - // RSTACK frames have the ncp ASH version in the first data field byte, - // and the reset reason in the second byte - if (this.rxSHBuffer[1] !== ASH_VERSION) { - return this.hostDisconnect(EzspStatus.ASH_ERROR_VERSION); - } - - // Ignore a RSTACK if the reset reason doesn't match our reset method - if (this.rxSHBuffer[2] !== NcpFailedCode.RESET_SOFTWARE) { - return EzspStatus.ASH_IN_PROGRESS; - } - - this.ncpError = EzspStatus.NO_ERROR; - - this.stopAckTimer(); - - this.timeouts = 0; - - this.setAckPeriod(CONFIG_ACK_TIME_INIT); - - this.flags = Flag.CONNECTED | Flag.ACK; - - console.log(`======== ASH connected ========`); - - return EzspStatus.SUCCESS; - } else if (frameType === AshFrameType.ERROR) { - return this.ncpDisconnect(this.rxSHBuffer[2]); - } - - return EzspStatus.ASH_IN_PROGRESS; - } - - // Connected - process the ackNum in ACK, NAK and DATA frames - if ((frameType === AshFrameType.DATA) || (frameType === AshFrameType.ACK) || (frameType === AshFrameType.NAK) ) { - ackNum = ashGetACKNum(this.rxSHBuffer[0]); - - debug(`<--- [FRAME type=${frameTypeStr} ackNum=${ackNum}]`); - - if (!withinRange(this.ackRx, ackNum, this.frmTx)) { - this.counters.rxBadAckNumber += 1; - - debug(`<-x- [FRAME type=${frameTypeStr} ackNum=${ackNum}] Invalid ACK num; not within <${this.ackRx}-${this.frmTx}>`); - - frameType = AshFrameType.INVALID; - } else if (ackNum !== this.ackRx) { - // new frame(s) ACK'ed? - this.ackRx = ackNum; - this.timeouts = 0; - - if (this.flags & Flag.RETX) { - // start timer if unACK'ed frames - this.stopAckTimer(); - - if (ackNum !== this.frmReTx) { - this.startAckTimer(); - } - } else { - this.adjustAckPeriod(false);// factor ACK time into period - - if (ackNum !== this.frmTx) { - // if more unACK'ed frames, - this.startAckTimer();// then restart ACK timer - } - - this.scrubReTxQueue();// free buffer(s) in ReTx queue - } - } - } - - // Process frames received while connected - switch (frameType) { - case AshFrameType.DATA: - frmNum = ashGetFrmNum(this.rxSHBuffer[0]); - const frameStr = `[FRAME type=${frameTypeStr} ackNum=${ackNum} frmNum=${frmNum}]`; - - if (frmNum === this.frmRx) { - // is frame in sequence? - if (this.rxDataBuffer == null) { - // valid frame but no memory? - this.counters.rxNoBuffer += 1; - - debug(`<-x- ${frameStr} No buffer available`); - - this.rejectFrame(); - - return EzspStatus.NO_RX_SPACE; - } - - if (this.rxSHBuffer[0] & ASH_RFLAG_MASK) { - // if retransmitted, force ACK - this.flags |= Flag.ACK; - } - - this.flags &= ~(Flag.REJ | Flag.NAK);// clear the REJ condition - this.frmRx = inc8(this.frmRx); - - this.randomizeBuffer(this.rxDataBuffer.data, this.rxDataBuffer.len);// IN/OUT data - this.rxQueue.addTail(this.rxDataBuffer);// add frame to receive queue - - debug(`<--- ${frameStr} Added to rxQueue`); - - this.counters.rxData += this.rxDataBuffer.len; - - setImmediate(() => { - this.emit(AshEvents.frame); - }); - return EzspStatus.SUCCESS; - } else { - // frame is out of sequence - if (this.rxSHBuffer[0] & ASH_RFLAG_MASK) { - // if retransmitted, force ACK - this.counters.rxDuplicates += 1; - this.flags |= Flag.ACK; - } else { - // 1st OOS? then set REJ, send NAK - if ((this.flags & Flag.REJ) === 0) { - this.counters.rxOutOfSequence += 1; - - debug(`<-x- ${frameStr} Out of sequence: expected ${this.frmRx}; got ${frmNum}.`); - } - - this.rejectFrame(); - } - } - break; - case AshFrameType.ACK: - // already fully processed - break; - case AshFrameType.NAK: - // start retransmission if needed - this.startRetransmission(); - - break; - case AshFrameType.RSTACK: - // unexpected ncp reset - this.ncpError = this.rxSHBuffer[2]; - - return this.hostDisconnect(EzspStatus.ASH_ERROR_NCP_RESET); - case AshFrameType.ERROR: - // ncp error - return this.ncpDisconnect(this.rxSHBuffer[2]); - case AshFrameType.INVALID: - // reject invalid frames - debug(`<-x- [FRAME type=${frameTypeStr}] Rejecting. ${this.rxSHBuffer.toString('hex')}`); - - this.rejectFrame(); - break; - } - - return EzspStatus.ASH_IN_PROGRESS; - } - - /** - * If the last control byte received was a DATA control, and we are connected and not already in the reject condition, - * then send a NAK and set the reject condition. - */ - private rejectFrame(): void { - if (((this.rxSHBuffer[0] & ASH_DFRAME_MASK) === AshFrameType.DATA) - && ((this.flags & (Flag.REJ | Flag.CONNECTED)) === Flag.CONNECTED) ) { - this.flags |= (Flag.REJ | Flag.NAK); - } - } - - /** - * Retrieve and process serial bytes. - * @returns - */ - private readFrame(buffer: Buffer): EzspStatus { - let status: EzspStatus; - let index: number = 0; - // let inByte: number = 0x00; - let outByte: number = 0x00; - - if (!this.decodeInProgress) { - this.rxLen = 0; - this.rxDataBuffer = null; - } - - for (const inByte of buffer) { - // 0xFF byte signals a callback is pending when between frames in synchronous (polled) callback mode. - if (!this.decodeInProgress && (inByte === ASH_WAKE)) { - if (this.ncpSleepEnabled) { - this.ncpHasCallbacks = true; - } - - status = EzspStatus.ASH_IN_PROGRESS; - continue; - } - - // Decode next input byte - note that many input bytes do not produce - // an output byte. Return on any error in decoding. - index = this.rxLen; - [status, outByte, this.rxLen] = this.decodeByte(inByte, outByte, this.rxLen); - - // discard an invalid frame - if ((status !== EzspStatus.ASH_IN_PROGRESS) && (status !== EzspStatus.SUCCESS)) { - this.freeNonNullRxBuffer(); - - break; - } - - // if input byte produced an output byte - if (this.rxLen !== index) { - if (this.rxLen <= SH_RX_BUFFER_LEN) { - // if a short frame, return in rxBuffer - this.rxSHBuffer[index] = outByte; - } else { - // if a longer DATA frame, allocate an EzspBuffer for it. - // (Note the control byte is always returned in shRxBuffer[0]. - // Even if no buffer can be allocated, the control's ackNum must be processed.) - if (this.rxLen === (SH_RX_BUFFER_LEN + 1)) { - // alloc buffer, copy prior data - this.rxDataBuffer = this.rxFree.allocBuffer(); - - if (this.rxDataBuffer !== null) { - // const len = SH_RX_BUFFER_LEN - 1; - - // (void) memcpy(this.rxDataBuffer.data, this.shRxBuffer + 1, SH_RX_BUFFER_LEN - 1); - this.rxSHBuffer.copy(this.rxDataBuffer.data, 0, 1, SH_RX_BUFFER_LEN); - - this.rxDataBuffer.len = SH_RX_BUFFER_LEN - 1; - } - } - - if (this.rxDataBuffer !== null) { - // copy next byte to buffer - this.rxDataBuffer.data[index - 1] = outByte;// -1 since control is omitted - this.rxDataBuffer.len = index; - } - } - } - - if (status !== EzspStatus.ASH_IN_PROGRESS) { - break; - } - } - - return status; - } - - /** - * - */ - private freeNonNullRxBuffer(): void { - if (this.rxDataBuffer !== null) { - this.rxFree.freeBuffer(this.rxDataBuffer); - - this.rxDataBuffer = null; - } - } - - /** - * - */ - private scrubReTxQueue(): void { - let buffer: EzspBuffer; - - while (this.ackRx !== this.frmReTxHead) { - buffer = this.reTxQueue.removeHead(); - - this.txFree.freeBuffer(buffer); - - this.frmReTxHead = inc8(this.frmReTxHead); - } - } - - /** - * If not already retransmitting, and there are unacked frames, start retransmitting after the last frame that was acked. - */ - private startRetransmission(): void { - if (!(this.flags & Flag.RETX) && (this.ackRx != this.frmTx) ) { - this.stopAckTimer(); - - this.frmReTx = this.ackRx; - this.flags |= (Flag.RETX | Flag.CAN); - } - } - - /** - * Check free rx buffers to see whether able to receive DATA frames: set or clear NR flag appropriately. - * Inform ncp of our status using the nFlag in ACKs and NAKs. - * Note that not ready status must be refreshed if it persists beyond a maximum time limit. - */ - private dataFrameFlowControl(): void { - if (this.flags & Flag.CONNECTED) { - // Set/clear NR flag based on the number of buffers free - if (this.rxFree.length < CONFIG_NR_LOW_LIMIT) { - this.flags |= Flag.NR; - - debug(`NOT READY - Signaling NCP`); - } else if (this.rxFree.length > CONFIG_NR_HIGH_LIMIT) { - this.flags &= ~Flag.NR; - - this.stopNrTimer(); // needed?? - // debug(`READY - Signaling NCP`);// spams-a-lot - } - - // Force an ACK (or possibly NAK) if we need to send an updated nFlag - // due to either a changed NR status or to refresh a set nFlag - if (this.flags & Flag.NR) { - if (!(this.flags & Flag.NRTX) || this.nrTimerHasExpired()) { - this.flags |= Flag.ACK; - - this.startNrTimer(); - } - } else { - this.nrTimerHasExpired();// ensure timer checked often - - if (this.flags & Flag.NRTX) { - this.flags |= Flag.ACK; - - this.stopNrTimer();// needed??? - } - } - } else { - this.stopNrTimer(); - - this.flags &= ~(Flag.NRTX | Flag.NR); - } - } - - /** - * Sets a fatal error state at the Host level. - * @param error - * @returns EzspStatus.HOST_FATAL_ERROR - */ - private hostDisconnect(error: EzspStatus): EzspStatus { - this.flags = 0; - this.hostError = error; - - console.error(`ASH disconnected: ${EzspStatus[error]} | NCP status: ${EzspStatus[this.ncpError]}`); - - this.emit(AshEvents.hostError, error); - - return EzspStatus.HOST_FATAL_ERROR; - } - - /** - * Sets a fatal error state at the NCP level. Will require a reset. - * @param error - * @returns EzspStatus.ASH_NCP_FATAL_ERROR - */ - private ncpDisconnect(error: EzspStatus): EzspStatus { - this.flags = 0; - this.ncpError = error; - - console.error(`ASH disconnected: ${EzspStatus[error]} | NCP status: ${EzspStatus[this.ncpError]}`); - - this.emit(AshEvents.ncpError, error); - - return EzspStatus.ASH_NCP_FATAL_ERROR; - } - - /** - * Same as randomizeArray(0, buffer, len). - * Returns buffer as-is if randomize is OFF. - * @param buffer IN/OUT - * @param len - */ - public randomizeBuffer(buffer: Buffer, len: number): void { - // If enabled, exclusive-OR buffer data with a pseudo-random sequence - if (CONFIG_RANDOMIZE) { - this.randomizeArray(0, buffer, len);// zero inits the random sequence - } - } - - /** - * Randomizes array contents by XORing with an 8-bit pseudo random sequence. - * This reduces the likelihood that byte-stuffing will greatly increase the size of the payload. - * (This could happen if a DATA frame contained repeated instances of the same reserved byte value.) - * - * @param seed zero initializes the random sequence a non-zero value continues from a previous invocation - * @param buf IN/OUT pointer to the array whose contents will be randomized - * @param len number of bytes in the array to modify - * @returns last value of the sequence. - * If a buffer is processed in two or more chunks, as with linked buffers, - * this value should be passed back as the value of the seed argument - */ - public randomizeArray(seed: number, buf: Buffer, len: number): number { - let outIdx = 0; - - if (seed === 0) { - seed = LFSR_SEED; - } - - while (len--) { - // *buf++ ^= seed; - buf[outIdx++] ^= seed; - - seed = (seed & 1) ? ((seed >> 1) ^ LFSR_POLY) : (seed >> 1); - } - - return seed; - } - - /** - * Get the frame type from the control byte and validate it against the frame length. - * @param control - * @param len Frame length - * @returns AshFrameType.INVALID if bad control/length otherwise the frame type. - */ - public getFrameType(control: number, len: number): AshFrameType { - if (control === AshFrameType.RSTACK) { - if (len === ASH_FRAME_LEN_RSTACK) { - return AshFrameType.RSTACK; - } - } else if (control === AshFrameType.ERROR) { - if (len === ASH_FRAME_LEN_ERROR) { - return AshFrameType.ERROR; - } - } else if ( (control & ASH_DFRAME_MASK) === AshFrameType.DATA) { - if (len >= ASH_FRAME_LEN_DATA_MIN) { - return AshFrameType.DATA; - } - } else if ( (control & ASH_SHFRAME_MASK) === AshFrameType.ACK) { - if (len === ASH_FRAME_LEN_ACK) { - return AshFrameType.ACK; - } - } else if ( (control & ASH_SHFRAME_MASK) === AshFrameType.NAK) { - if (len === ASH_FRAME_LEN_NAK) { - return AshFrameType.NAK; - } - } else { - this.counters.rxBadControl += 1; - debug(`Frame illegal control ${control}.`);// EzspStatus.ASH_BAD_CONTROL - - return AshFrameType.INVALID; - } - - this.counters.rxBadLength += 1; - debug(`Frame illegal length ${len} for control ${control}.`);// EzspStatus.ASH_BAD_LENGTH - - return AshFrameType.INVALID; - } - - /** - * Encode byte for sending. - * @param len Start a new frame if non-zero - * @param byte - * @returns outByte - */ - private encodeByte(len: number, byte: number): number { - // start a new frame if len is non-zero - if (len) { - this.encodeCount = len; - this.txOffset = 0; - this.encodeState = 0; - this.encodeEscFlag = false; - this.encodeCrc = 0xFFFF; - } - - // was an escape last time? - if (this.encodeEscFlag) { - this.encodeEscFlag = false; - - // send data byte with bit flipped - return this.encodeFlip; - } - - // control and data field bytes - if (this.encodeState === 0) { - this.encodeCrc = halCommonCrc16(byte, this.encodeCrc); - - if (--this.encodeCount === 0) { - this.encodeState = 1; - } else { - ++this.txOffset; - } - - return this.encodeStuffByte(byte); - } else if (this.encodeState === 1) { - // CRC high byte - this.encodeState = 2; - - return this.encodeStuffByte(this.encodeCrc >> 8); - } else if (this.encodeState === 2) { - // CRC low byte - this.encodeState = 3; - - return this.encodeStuffByte(this.encodeCrc & 0xFF); - } - - this.txOffset = 0xFF; - - return AshReservedByte.FLAG; - } - - /** - * Stuff byte as defined by ASH protocol. - * @param byte - * @returns - */ - private encodeStuffByte(byte: number): number { - if (AshReservedByte[byte] != null) { - // is special byte - this.encodeEscFlag = true; - this.encodeFlip = byte ^ ASH_FLIP; - - return AshReservedByte.ESCAPE; - } else { - return byte; - } - } - - /** - * Decode received byte. - * @param byte - * @param inByte IN/OUT - * @param inLen IN/OUT - * @returns [EzspStatus, outByte, outLen] - * - EzspStatus.ASH_IN_PROGRESS - * - EzspStatus.ASH_COMM_ERROR - * - EzspStatus.ASH_BAD_CRC - * - EzspStatus.ASH_TOO_SHORT - * - EzspStatus.ASH_TOO_LONG - * - EzspStatus.SUCCESS - * - EzspStatus.ASH_CANCELLED - * - EzspStatus.ASH_ERROR_XON_XOFF - */ - private decodeByte(byte: number, inByte: number, inLen: number): [EzspStatus, outByte: number, outLen: number] { - let status: EzspStatus = EzspStatus.ASH_IN_PROGRESS; - - if (!this.decodeInProgress) { - this.decodeLen = 0; - this.decodeByte1 = 0; - this.decodeByte2 = 0; - this.decodeFlip = 0; - this.decodeCrc = 0xFFFF; - } - - switch (byte) { - case AshReservedByte.FLAG: - // flag byte (frame delimiter) - if (this.decodeLen === 0) { - // if no frame data, not end flag, so ignore it - this.decodeFlip = 0;// ignore isolated data escape between flags - break; - } else if (this.decodeLen === 0xFF) { - status = EzspStatus.ASH_COMM_ERROR; - } else if (this.decodeCrc !== ((this.decodeByte2 << 8) + this.decodeByte1)) { - status = EzspStatus.ASH_BAD_CRC; - } else if (this.decodeLen < ASH_MIN_FRAME_WITH_CRC_LEN) { - status = EzspStatus.ASH_TOO_SHORT; - } else if (this.decodeLen > ASH_MAX_FRAME_WITH_CRC_LEN) { - status = EzspStatus.ASH_TOO_LONG; - } else { - status = EzspStatus.SUCCESS; - } - break; - case AshReservedByte.ESCAPE: - // byte stuffing escape byte - this.decodeFlip = ASH_FLIP; - break; - case AshReservedByte.CANCEL: - // cancel frame without an error - status = EzspStatus.ASH_CANCELLED; - break; - case AshReservedByte.SUBSTITUTE: - // discard remainder of frame - this.decodeLen = 0xFF;// special value flags low level comm error - break; - case AshReservedByte.XON: - case AshReservedByte.XOFF: - // If host is using RTS/CTS, ignore any XON/XOFFs received from the NCP. - // If using XON/XOFF, the host driver must remove them from the input stream. - // If it doesn't, it probably means the driver isn't setup for XON/XOFF, - // so issue an error to flag the serial port driver problem. - if (this.serialPort != null && !this.serialPort.settings.rtscts) { - status = EzspStatus.ASH_ERROR_XON_XOFF; - } - break; - default: - // a normal byte - byte ^= this.decodeFlip; - this.decodeFlip = 0; - - if (this.decodeLen <= ASH_MAX_FRAME_WITH_CRC_LEN) { - // limit length to max + 1 - ++this.decodeLen; - } - - if (this.decodeLen > ASH_CRC_LEN) { - // compute frame CRC even if too long - this.decodeCrc = halCommonCrc16(this.decodeByte2, this.decodeCrc); - - if (this.decodeLen <= ASH_MAX_FRAME_WITH_CRC_LEN) { - // store to only max len - inByte = this.decodeByte2; - inLen = this.decodeLen - ASH_CRC_LEN;// CRC is not output, reduce length - } - } - - this.decodeByte2 = this.decodeByte1; - this.decodeByte1 = byte; - break; - } - - this.decodeInProgress = (status === EzspStatus.ASH_IN_PROGRESS); - - return [status, inByte, inLen]; - } - - /** - * Starts the Not Ready timer - * - * On the host, this times nFlag refreshing when the host doesn't have room for callbacks for a prolonged period. - * - * On the NCP, if this times out the NCP resumes sending callbacks. - */ - private startNrTimer(): void { - this.nrTimer = (Date.now() + CONFIG_NR_TIME); - } - - /** - * Stop Not Ready timer (set to 0). - */ - private stopNrTimer(): void { - this.nrTimer = 0; - } - - /** - * Tests whether the Not Ready timer has expired or has stopped. If expired, it is stopped. - * - * @returns true if the Not Ready timer has expired or stopped - */ - private nrTimerHasExpired(): boolean { - if (this.nrTimer) { - if ((Date.now() - this.nrTimer) >= 0) { - this.nrTimer = 0; - } - } - - return (!this.nrTimer); - } - - /** - * Indicates whether or not Not Ready timer is currently running. - * - * @return True if nrTime == 0 - */ - private nrTimerIsNotRunning(): boolean { - return this.nrTimer === 0; - } - - /** - * Sets the acknowledgement timer period (in msec) and stops the timer. - */ - private setAckPeriod(msec: number): void { - this.ackPeriod = msec; - this.ackTimer = 0; - } - - /** - * Sets the acknowledgement timer period (in msec), and starts the timer running. - */ - private setAndStartAckTimer(msec: number): void { - this.setAckPeriod(msec); - this.startAckTimer(); - } - - /** - * Adapts the acknowledgement timer period to the observed ACK delay. - * If the timer is not running, it does nothing. - * If the timer has expired, the timeout period is doubled. - * If the timer has not expired, the elapsed time is fed into simple - * - * IIR filter: - * T[n+1] = (7*T[n] + elapsedTime) / 8 - * - * The timeout period, ackPeriod, is limited such that: - * config.ackTimeMin <= ackPeriod <= config.ackTimeMax. - * - * The acknowledgement timer is always stopped by this function. - * - * @param expired true if timer has expired - */ - private adjustAckPeriod(expired: boolean): void { - if (expired) { - // if expired, double the period - this.ackPeriod += this.ackPeriod; - } else if (this.ackTimer) { - // adjust period only if running - // time elapsed since timer was started - let temp: number = this.ackPeriod; - // compute time to receive acknowledgement, then stop timer - const lastAckTime: number = Date.now() - this.ackTimer; - temp = (temp << 3) - temp; - temp += lastAckTime << 2; - temp >>= 3; - this.ackPeriod = (temp & 0xFFFF); - } - - // keep ackPeriod within limits - if (this.ackPeriod > CONFIG_ACK_TIME_MAX) { - this.ackPeriod = CONFIG_ACK_TIME_MAX; - } else if (this.ackPeriod < CONFIG_ACK_TIME_MIN) { - this.ackPeriod = CONFIG_ACK_TIME_MIN; - } - - this.ackTimer = 0;// always stop the timer - } - - /** - * Sets ACK Timer to the specified period and starts it running. - */ - private startAckTimer(): void { - this.ackTimer = Date.now(); - } - - /** - * Stops and clears ACK Timer. - */ - private stopAckTimer(): void { - this.ackTimer = 0; - } - - /** - * Indicates whether or not ACK Timer has expired. - * If the timer is stopped (0) then it is not expired. - * - * @returns - */ - private ackTimerHasExpired(): boolean { - if (this.ackTimer === 0) { - // if timer is not running, return false - return false; - } - - // return ((halCommonGetInt16uMillisecondTick() - this.ackTimer) >= this.ackPeriod); - return ((Date.now() - this.ackTimer) >= this.ackPeriod); - } - - /** - * Indicates whether or not ACK Timer is currently running (!= 0). - * The timer may be running even if expired. - */ - private ackTimerIsNotRunning(): boolean { - return this.ackTimer === 0; - } - - /** - * Increase counters based on frame type and direction. - * @param sent True if frame being sent, false if being received. - */ - private countFrame(sent: boolean): void { - let control: number; - - if (sent) { - control = this.txSHBuffer[0]; - this.counters.txAllFrames += 1; - } else { - control = this.rxSHBuffer[0]; - this.counters.rxAllFrames += 1; - } - - if ((control & ASH_DFRAME_MASK) === AshFrameType.DATA) { - if (sent) { - if (control & ASH_RFLAG_MASK) { - this.counters.txReDataFrames += 1; - } else { - this.counters.txDataFrames += 1; - } - } else { - if (control & ASH_RFLAG_MASK) { - this.counters.rxReDataFrames += 1; - } else { - this.counters.rxDataFrames += 1; - } - } - } else if ((control & ASH_SHFRAME_MASK) === AshFrameType.ACK) { - if (sent) { - this.counters.txAckFrames += 1; - - if (control & ASH_NFLAG_MASK) { - this.counters.txN1Frames += 1; - }/* else { - this.counters.txN0Frames += 1; - }*/ - } else { - this.counters.rxAckFrames += 1; - - if (control & ASH_NFLAG_MASK) { - this.counters.rxN1Frames += 1; - }/* else { - this.counters.rxN0Frames += 1; - }*/ - } - } else if ((control & ASH_SHFRAME_MASK) === AshFrameType.NAK) { - if (sent) { - this.counters.txNakFrames += 1; - - if (control & ASH_NFLAG_MASK) { - this.counters.txN1Frames += 1; - }/* else { - this.counters.txN0Frames += 1; - }*/ - } else { - this.counters.rxNakFrames += 1; - - if (control & ASH_NFLAG_MASK) { - this.counters.rxN1Frames += 1; - }/* else { - this.counters.rxN0Frames += 1; - }*/ - } - } - } - - /** - * Prints counters in a nicely formatted table. - */ - private printCounters(): void { - console.table({ - "Total frames": {"Received": this.counters.rxAllFrames, "Transmitted": this.counters.txAllFrames}, - "Cancelled": {"Received": this.counters.rxCancelled, "Transmitted": this.counters.txCancelled}, - "DATA frames": {"Received": this.counters.rxDataFrames, "Transmitted": this.counters.txDataFrames}, - "DATA bytes": {"Received": this.counters.rxData, "Transmitted": this.counters.txData}, - "Retry frames": {"Received": this.counters.rxReDataFrames, "Transmitted": this.counters.txReDataFrames}, - "ACK frames": {"Received": this.counters.rxAckFrames, "Transmitted": this.counters.txAckFrames}, - "NAK frames": {"Received": this.counters.rxNakFrames, "Transmitted": this.counters.txNakFrames}, - "nRdy frames": {"Received": this.counters.rxN1Frames, "Transmitted": this.counters.txN1Frames}, - }); - - console.table({ - "CRC errors": {"Received": this.counters.rxCrcErrors}, - "Comm errors": {"Received": this.counters.rxCommErrors}, - "Length < minimum": {"Received": this.counters.rxTooShort}, - "Length > maximum": {"Received": this.counters.rxTooLong}, - "Bad controls": {"Received": this.counters.rxBadControl}, - "Bad lengths": {"Received": this.counters.rxBadLength}, - "Bad ACK numbers": {"Received": this.counters.rxBadAckNumber}, - "Out of buffers": {"Received": this.counters.rxNoBuffer}, - "Retry dupes": {"Received": this.counters.rxDuplicates}, - "Out of sequence": {"Received": this.counters.rxOutOfSequence}, - "ACK timeouts": {"Received": this.counters.rxAckTimeouts}, - }); - } -} diff --git a/src/adapter/ember/uart/consts.ts b/src/adapter/ember/uart/consts.ts deleted file mode 100644 index ffd32a44fa5..00000000000 --- a/src/adapter/ember/uart/consts.ts +++ /dev/null @@ -1,115 +0,0 @@ -import {EZSP_MAX_FRAME_LENGTH, EZSP_MIN_FRAME_LENGTH} from "../ezsp/consts"; - -/** - * Define the size of the receive buffer pool on the EZSP host. - * - * The number of receive buffers does not need to be greater than the number of packet buffers available on the NCP, - * because this in turn is the maximum number of callbacks that could be received between commands. - * In reality a value of 20 is a generous allocation. - */ -export const EZSP_HOST_RX_POOL_SIZE = 20; -/** - * The number of transmit buffers must be set to the number of receive buffers - * -- to hold the immediate ACKs sent for each callabck frame received -- - * plus 3 buffers for the retransmit queue and one each for an automatic ACK - * (due to data flow control) and a command. - */ -export const TX_POOL_BUFFERS = (EZSP_HOST_RX_POOL_SIZE + 5); - - -/** protocol version */ -export const ASH_VERSION = 2; - -/** - * Timeouts before link is judged down. - * - * Consecutive ACK timeouts (minus 1) needed to enter the ERROR state. - * - * Is 3 in ash-ncp.h - */ -export const ASH_MAX_TIMEOUTS = 6; -/** max time in msecs for ncp to wake */ -export const ASH_MAX_WAKE_TIME = 150; - -/** - * Define the units used by the Not Ready timer as 2**n msecs - * log2 of msecs per NR timer unit - */ -export const ASH_NR_TIMER_BIT = 4; - -/** Control byte mask for DATA frame */ -export const ASH_DFRAME_MASK = 0x80; -/** Control byte mask for short frames (ACK/NAK) */ -export const ASH_SHFRAME_MASK = 0xE0; - -/** Acknowledge frame number */ -export const ASH_ACKNUM_MASK = 0x07; -export const ASH_ACKNUM_BIT = 0; -/** Retransmitted frame flag */ -export const ASH_RFLAG_MASK = 0x08; -export const ASH_RFLAG_BIT = 3; -/** Receiver not ready flag */ -export const ASH_NFLAG_MASK = 0x08; -export const ASH_NFLAG_BIT = 3; -/** Reserved for future use */ -export const ASH_PFLAG_MASK = 0x10; -export const ASH_PFLAG_BIT = 4; -/** DATA frame number */ -export const ASH_FRMNUM_MASK = 0x70; -export const ASH_FRMNUM_BIT = 4; - - -/** - * The wake byte special function applies only when in between frames, - * so it does not need to be escaped within a frame. - * (also means NCP data pending) - */ -export const ASH_WAKE = 0xFF; /*!< */ - -/** Constant used in byte-stuffing (XOR mask used in byte stuffing) */ -export const ASH_FLIP = 0x20; - - -// Field and frame lengths, excluding flag byte and any byte stuffing overhead -// All limits are inclusive -export const ASH_MIN_DATA_FIELD_LEN = EZSP_MIN_FRAME_LENGTH; -export const ASH_MAX_DATA_FIELD_LEN = EZSP_MAX_FRAME_LENGTH; -/** with control */ -export const ASH_MIN_DATA_FRAME_LEN = (ASH_MIN_DATA_FIELD_LEN + 1); -/** control plus data field, but not CRC */ -export const ASH_MIN_FRAME_LEN = 1; -export const ASH_MAX_FRAME_LEN = (ASH_MAX_DATA_FIELD_LEN + 1); -export const ASH_CRC_LEN = 2; -export const ASH_MIN_FRAME_WITH_CRC_LEN = (ASH_MIN_FRAME_LEN + ASH_CRC_LEN); -export const ASH_MAX_FRAME_WITH_CRC_LEN = (ASH_MAX_FRAME_LEN + ASH_CRC_LEN); - - -// Lengths for each frame type: includes control and data field (if any), excludes the CRC and flag bytes -/** ash frame len data min */ -export const ASH_FRAME_LEN_DATA_MIN = (ASH_MIN_DATA_FIELD_LEN + 1); -/** [control] */ -export const ASH_FRAME_LEN_ACK = 1; -/** [control] */ -export const ASH_FRAME_LEN_NAK = 1; -/** [control] */ -export const ASH_FRAME_LEN_RST = 1; -/** [control, version, reset reason] */ -export const ASH_FRAME_LEN_RSTACK = 3; -/** [control, version, error] */ -export const ASH_FRAME_LEN_ERROR = 3; - - -// Define lengths of short frames - includes control byte and data field -/** longest non-data frame sent */ -export const SH_TX_BUFFER_LEN = 2; -/** longest non-data frame received */ -export const SH_RX_BUFFER_LEN = 3; - - -// Define constants for the LFSR in randomizeBuffer() -/** polynomial */ -export const LFSR_POLY = 0xB8; -/** initial value (seed) */ -export const LFSR_SEED = 0x42; - -export const VALID_BAUDRATES = [600,1200,2400,4800,9600,19200,38400,57600,115200,230400,460800]; diff --git a/src/adapter/ember/uart/enums.ts b/src/adapter/ember/uart/enums.ts deleted file mode 100644 index a9c9fb06f19..00000000000 --- a/src/adapter/ember/uart/enums.ts +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Identify the type of frame from control byte. - * - * Control byte formats - * +---------+----+----+----+----+----+----+----+----++---------+ - * | | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 || Range | - * +---------+----+----+----+----+----+----+----+----++---------+ - * | DATA | 0 | frameNum | rF | ackNum ||0x00-0x7F| - * +---------+----+----+----+----+----+----+----+----++---------+ - * | ACK | 1 | 0 | 0 | pF | nF | ackNum ||0x80-0x9F| - * | NAK | 1 | 0 | 1 | pF | nF | ackNum ||0xA0-0xBF| - * +---------+----+----+----+----+----+----+----+----++---------+ - * | RST | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 || 0xC0 | - * | RSTACK | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 || 0xC1 | - * | ERROR | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 || 0xC2 | - * +---------+----+----+----+----+----+----+----+----++---------+ - * rF = rFlag (retransmission flag) - * nF = nFlag (receiver not ready flag, always 0 in frames sent by the NCP) - * pF = flag reserved for future use - * frameNum = DATA frame’s 3-bit sequence number - * ackNum = acknowledges receipt of DATA frames up to, but not including, ackNum - * Control byte values 0xC3-0xFE are unused, 0xFF is reserved. - */ -export enum AshFrameType { - INVALID = -1, - /** - * Carries all EZSP frames. - * - * [CONTROL, EZSP 0, EZSP 1, EZSP 2, EZSP n, CRC high, CRC low, FLAG] - * - * Notation used in documentation: DATA(F, A, R) - * - F: frame number (frmNum) - * - A: acknowledge number (ackNum) - * - R: retransmit flag (reTx) - * - * Example without pseudo-random sequence applied to Data Field: - * - EZSP “version” command: 00 00 00 02 - * - DATA(2, 5, 0) = 25 00 00 00 02 1A AD 7E - * - EZSP “version” response: 00 80 00 02 02 11 30 - * - DATA(5, 3, 0) = 53 00 80 00 02 02 11 30 63 16 7E - * - * Example with pseudo-random sequence applied to Data Field: - * - EZSP “version” command: 00 00 00 02 - * - DATA(2, 5, 0) = 25 42 21 A8 56 A6 09 7E - * - EZSP “version” response: 00 80 00 02 02 11 30 - * - DATA(5, 3, 0) = 53 42 A1 A8 56 28 04 82 96 23 7E - * - * Sent by: NCP, Host - */ - DATA = 0, - /** - * Acknowledges receipt of a valid DATA frame. - * - * [CONTROL, CRC high, CRC low, FLAG] - * - * Notation used in documentation: ACK(A)+/- - * - A: acknowledge number (ackNum) - * - +/-: not ready flag (nRdy); “+” = “0” = “ready”; “-” = “1” = “not ready” - * - * Examples: - * - ACK(1)+ :81 60 59 7E - * - ACK(6)– : 8E 91 B6 7E - * - * Sent by: NCP, Host - */ - ACK = 0x80,// 0b10000000 - /** - * Indicates receipt of a DATA frame with an error or that was discarded due to lack of memory. - * - * [CONTROL, CRC high, CRC low, FLAG] - * - * Notation used in documentation: NAK(A)+/- - * - A: acknowledge number (ackNum) - * - +/-: not ready flag (nRdy); “+” = “0” = “ready”; “-” = “1” = “not ready” - * - * Examples: - * - NAK(6)+ : A6 34 DC 7E - * - NAK(5)- : AD 85 B7 7E - * - * Sent by: NCP, Host - */ - NAK = 0xA0,// 0b10100000 - /** - * Requests the NCP to perform a software reset (valid even if the NCP is in the FAILED state). - * - * [CONTROL, CRC high, CRC low, FLAG] - * - * Notation used in documentation: RST() - * - * Example: C0 38 BC 7E - * - * Sent by: Host - */ - RST = 0xC0,// 0b11000000 - /** - * Informs the Host that the NCP has reset and the reason for the reset. - * - * [CONTROL, version, reset code, CRC high, CRC low, FLAG] - * - * Notation used in documentation: RSTACK(V, C) - * - V: version - * - C: reset code - * - * Example: C1 02 02 9B 7B 7E - * - * Sent by: NCP - */ - RSTACK = 0xC1,// 0b11000001 - /** - * Informs the Host that the NCP detected a fatal error and is in the FAILED state. - * - * [CONTROL, version, error code, CRC high, CRC low, FLAG] - * - * Notation used in documentation: ERROR(V, C ) - * - V: version - * - C: reset code - * - * Example: C2 01 52 FA BD 7E - * - * Sent by: NCP - */ - ERROR = 0xC2,// 0b11000010 -} - -export enum AshReservedByte { - /** - * Marks the end of a frame. - * - * When a Flag Byte is received, the data received since the last Flag Byte or Cancel Byte - * is tested to see whether it is a valid frame. */ - FLAG = 0x7E, - /** - * Indicates that the following byte is escaped. - * - * If the byte after the Escape Byte is not a reserved byte, - * bit 5 of the byte is complemented to restore its original value. - * If the byte after the Escape Byte is a reserved value, the Escape Byte has no effect. */ - ESCAPE = 0x7D, - /** - * Resume transmission. - * - * Used in XON/XOFF flow control. Always ignored if received by the NCP. - */ - XON = 0x11, - /** - * Stop transmission. - * - * Used in XON/XOFF flow control. Always ignored if received by the NCP - */ - XOFF = 0x13, - /** - * Replaces a byte received with a low-level communication error (e.g., framing error) from the UART. - * - * When a Substitute Byte is processed, the data between the previous and the next Flag Bytes is ignored. - */ - SUBSTITUTE = 0x18, - /** - * Terminates a frame in progress. - * - * A Cancel Byte causes all data received since the previous Flag Byte to be ignored. - * Note that as a special case, RST and RSTACK frames are preceded by Cancel Bytes to ignore any link startup noise. - */ - CANCEL = 0x1A, -} - -/** - * The NCP enters the FAILED state if it detects one of the following errors: - * - An abnormal internal reset due to an error, failed assertion, or fault. - * - Exceeding the maximum number of consecutive acknowledgement timeouts. - * - * When the NCP enters the FAILED state, the NCP sends an ERROR frame containing a reset or error code - * and will reply to all subsequent frames received, except RST, with an ERROR frame. - * To reinitialize the ASH protocol, the Host must reset the NCP by either asserting the nRESET pin or sending the RST frame. - * - * The codes are returned by the NCP in the: - * - Reset Code byte of a RSTACK frame - * - Error Code byte of an ERROR frame. - * - * Silicon Labs wireless mesh chips can detect numerous reset fault causes beyond those in the table. - * When sent to the host, these new reset codes have 0x80 added to the value returned by their HAL’s reset code. - */ -export enum NcpFailedCode { - RESET_UNKNOWN_REASON = 0, - RESET_EXTERNAL = 1, - RESET_POWERON = 2, - RESET_WATCHDOG = 3, - RESET_ASSERT = 6, - RESET_BOOTLOADER = 9, - RESET_SOFTWARE = 11, - ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, - CHIP_SPECIFIC_ERROR_RESET_CODE = 0x80, -} diff --git a/src/adapter/ember/uart/parser.ts b/src/adapter/ember/uart/parser.ts deleted file mode 100644 index f470fae900e..00000000000 --- a/src/adapter/ember/uart/parser.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import {Transform, TransformCallback, TransformOptions} from "stream"; -import {AshReservedByte} from "./enums"; - -const debug = Debug('zigbee-herdsman:adapter:ember:uart:ash:parser'); - -export class AshParser extends Transform { - private buffer: Buffer; - - public constructor(opts?: TransformOptions) { - super(opts); - - this.buffer = Buffer.alloc(0); - } - - _transform(chunk: Buffer, encoding: BufferEncoding, cb: TransformCallback): void { - let data = Buffer.concat([this.buffer, chunk]); - let position: number; - - while ((position = data.indexOf(AshReservedByte.FLAG)) !== -1) { - // emit the frame via 'data' event - const frame = data.subarray(0, position + 1); - - setImmediate((): void => { - debug(`<<<< [FRAME raw=${frame.toString('hex')}]`); - this.push(frame); - }); - - // remove the frame from internal buffer (set below) - data = data.subarray(position + 1); - } - - this.buffer = data; - - cb(); - } - - _flush(cb: TransformCallback): void { - this.push(this.buffer); - - this.buffer = Buffer.alloc(0); - - cb(); - } -} diff --git a/src/adapter/ember/uart/queues.ts b/src/adapter/ember/uart/queues.ts deleted file mode 100644 index 7acd382f0fc..00000000000 --- a/src/adapter/ember/uart/queues.ts +++ /dev/null @@ -1,243 +0,0 @@ -/* istanbul ignore file */ -import {EZSP_MAX_FRAME_LENGTH} from "../ezsp/consts"; - - -/** - * Buffer to hold a DATA frame. - * Allocates `data` to `EZSP_MAX_FRAME_LENGTH` filled with zeroes. - */ -export class EzspBuffer { - /** uint8_t[EZSP_MAX_FRAME_LENGTH] */ - public data: Buffer; - public link: EzspBuffer; - /** uint8_t */ - public len: number; - - constructor() { - this.data = Buffer.alloc(EZSP_MAX_FRAME_LENGTH);// inits to all-zeroes - this.link = null; - this.len = 0; - } -} - -/** - * Simple queue (singly-linked list) - */ -export class EzspQueue { - public tail: EzspBuffer; - - constructor() { - this.tail = null; - } - - /** - * Get the number of buffers in the queue. - * @returns - */ - get length(): number { - let head: EzspBuffer = this.tail; - let count: number = 0; - - for (count; head != null; count++) { - head = head.link; - } - - return count; - } - - get empty(): boolean { - return (this.tail == null); - } - - /** - * Get a pointer to the buffer at the head of the queue. - * @returns - */ - get head(): EzspBuffer { - let head: EzspBuffer = this.tail; - - if (head == null) { - throw new Error(`Tried to get head from an empty queue.`); - } - - if (head != null) { - while (head.link != null) { - head = head.link; - } - - } - - return head; - } - - /** - * Get a pointer to the Nth entry in the queue (the tail corresponds to N = 1). - * - * @param n - * @returns - */ - public getNthEntry(n: number): EzspBuffer { - if (n === 0) { - throw new Error(`Asked for 0th element in queue.`); - } - - let buf: EzspBuffer = this.tail; - - while (--n) { - if (buf == null) { - throw new Error(`Less than N entries in queue.`); - } - - buf = buf.link; - } - - return buf; - } - - /** - * Get a pointer to the entry before the specified entry (closer to the tail). - * If the buffer specified is null, the head entry is returned. - * If the buffer specified is the tail, null is returned. - * @param entry The buffer to look before. - * @returns - */ - public getPrecedingEntry(entry: EzspBuffer): EzspBuffer { - let buf: EzspBuffer = this.tail; - - if (buf === entry) { - return null; - } else { - do { - if (buf.link === entry) { - return buf; - } - - buf = buf.link; - } while (buf != null); - - throw new Error(`Buffer not in queue.`); - } - } - - /** - * Add a buffer to the tail of the queue. - * @param buf - */ - public addTail(buf: EzspBuffer): void { - if (buf) { - buf.link = this.tail; - this.tail = buf; - } else { - throw new Error(`Called addTail with null buffer`); - } - } - - /** - * Remove the buffer at the head of the queue. - * @returns The removed buffer. - */ - public removeHead(): EzspBuffer { - let head: EzspBuffer = this.tail; - - if (head == null) { - throw new Error(`Tried to remove head from an empty queue.`); - } - - if (head.link == null) { - this.tail = null; - } else { - let prev: EzspBuffer; - - do { - prev = head; - head = head.link; - } while (head.link != null); - - prev.link = null; - } - - return head; - } - - /** - * Remove the specified entry from the queue. - * @param entry - * @returns A pointer to the preceding entry (if any). - */ - public removeEntry(entry: EzspBuffer): EzspBuffer { - const buf: EzspBuffer = this.getPrecedingEntry(entry); - - if (buf != null) { - buf.link = entry.link; - } else { - this.tail = entry.link; - } - - return buf; - } -} - -/** - * Simple free list (singly-linked list) - */ -export class EzspFreeList { - public link: EzspBuffer; - - constructor() { - this.link = null; - } - - /** - * Get the number of buffers in the free list. - * @returns - */ - get length(): number { - let next: EzspBuffer = this.link; - let count: number = 0; - - for (count; next != null; count++) { - next = next.link; - } - - return count; - } - - /** - * Add a buffer to the free list. - * @param buf - */ - public freeBuffer(buf: EzspBuffer): void { - if (buf) { - buf.link = this.link; - this.link = buf; - } else { - throw new Error(`Called freeBuffer with null buffer`); - } - } - - /** - * Get a buffer from the free list. - * @returns - */ - public allocBuffer(): EzspBuffer { - const buf: EzspBuffer = this.link; - - if (buf != null) { - this.link = buf.link; - buf.len = 0; - - if (buf.data.length !== EZSP_MAX_FRAME_LENGTH) { - // should never happen if buffers are handled properly, but just in case, re-alloc to max length - buf.data = Buffer.alloc(EZSP_MAX_FRAME_LENGTH); - - const e = new Error(); - console.assert(false, `Pre-allocated buffer had improper size and had to be re-allocated. ${e.stack}`); - } else { - // (void) memset(buffer->data, 0, EZSP_MAX_FRAME_LENGTH); - buf.data.fill(0); - } - } - - return buf; - } -} diff --git a/src/adapter/ember/uart/writer.ts b/src/adapter/ember/uart/writer.ts deleted file mode 100644 index c577f94127f..00000000000 --- a/src/adapter/ember/uart/writer.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* istanbul ignore file */ -import Debug from "debug"; -import {Readable, ReadableOptions} from "stream"; - -const debug = Debug('zigbee-herdsman:adapter:ember:uart:ash:writer'); - - -export class AshWriter extends Readable { - private bytesToWrite: number[]; - - constructor(opts?: ReadableOptions) { - super(opts); - - this.bytesToWrite = []; - } - - private writeBytes(): void { - const buffer = Buffer.from(this.bytesToWrite); - this.bytesToWrite = []; - - debug(`>>>> [FRAME raw=${buffer.toString('hex')}]`); - - // this.push(buffer); - this.emit('data', buffer); - } - - public writeByte(byte: number): void { - this.bytesToWrite.push(byte); - } - - public writeAvailable(): boolean { - if (this.readableLength < this.readableHighWaterMark) { - return true; - } else { - this.writeFlush(); - - return false; - } - } - - /** - * If there is anything to send, send to the port. - */ - public writeFlush(): void { - if (this.bytesToWrite.length) { - this.writeBytes(); - } - } - - public _read(): void { - } -} diff --git a/src/adapter/ember/utils/initters.ts b/src/adapter/ember/utils/initters.ts deleted file mode 100644 index 2291c0361ef..00000000000 --- a/src/adapter/ember/utils/initters.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* istanbul ignore file */ -import {NetworkCache} from "../adapter/emberAdapter"; -import { - BLANK_EUI64, - UNKNOWN_NETWORK_STATE, - ZB_PSA_ALG, - INVALID_PAN_ID, - INVALID_RADIO_CHANNEL, - NULL_NODE_ID, - EMBER_ALL_802_15_4_CHANNELS_MASK, - BLANK_EXTENDED_PAN_ID -} from "../consts"; -import { - EmberJoinMethod, - EmberNetworkStatus, - SecManDerivedKeyType, - SecManFlag, - SecManKeyType -} from "../enums"; -import {EMBER_AES_HASH_BLOCK_SIZE} from "../ezsp/consts"; -import {EmberAesMmoHashContext, SecManContext} from "../types"; - - -/** - * Initialize a network cache index with proper "invalid" values. - * @returns - */ -export const initNetworkCache = (): NetworkCache => { - return { - eui64: BLANK_EUI64, - parameters: { - extendedPanId: BLANK_EXTENDED_PAN_ID.slice(),// copy - panId: INVALID_PAN_ID, - radioTxPower: 0, - radioChannel: INVALID_RADIO_CHANNEL, - joinMethod: EmberJoinMethod.MAC_ASSOCIATION, - nwkManagerId: NULL_NODE_ID, - nwkUpdateId: 0, - channels: EMBER_ALL_802_15_4_CHANNELS_MASK, - }, - status: UNKNOWN_NETWORK_STATE as EmberNetworkStatus, - }; -}; - -/** - * This routine will initialize a Security Manager context correctly for use in subsequent function calls. - * @returns - */ -export const initSecurityManagerContext = (): SecManContext => { - return { - coreKeyType: SecManKeyType.NONE, - keyIndex: 0, - derivedType: SecManDerivedKeyType.NONE, - eui64: `0x0000000000000000`, - multiNetworkIndex: 0, - flags: SecManFlag.NONE, - psaKeyAlgPermission: ZB_PSA_ALG,// unused for classic key storage - }; -}; - -/** - * This routine clears the passed context so that a new hash calculation - * can be performed. - * - * @returns context A pointer to the location of hash context to clear. - */ -export const aesMmoHashInit = (): EmberAesMmoHashContext => { - // MEMSET(context, 0, sizeof(EmberAesMmoHashContext)); - return { - result: Buffer.alloc(EMBER_AES_HASH_BLOCK_SIZE),// uint8_t[EMBER_AES_HASH_BLOCK_SIZE] - length: 0x00000000,// uint32_t - }; -}; diff --git a/src/adapter/ember/utils/math.ts b/src/adapter/ember/utils/math.ts deleted file mode 100644 index 98c3e6f9b70..00000000000 --- a/src/adapter/ember/utils/math.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* istanbul ignore file */ -//-------------------------------------------------------------- -// 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 */ -export const inc8 = (n: number): number => mod8(n + 1); -/** Return true if n is within the range lo through hi, computed (mod 8) */ -export const withinRange = (lo: number, n: number, hi: number): boolean => mod8(n - lo) <= mod8(hi - lo); - - -//-------------------------------------------------------------- -// CRC - -/** - * Calculates 16-bit cyclic redundancy code (CITT CRC 16). - * - * Applies the standard CITT CRC 16 polynomial to a - * single byte. It should support being called first with an initial - * value, then repeatedly until all data is processed. - * - * @param newByte The new byte to be run through CRC. - * @param prevResult The previous CRC result. - * @returns The new CRC result. - */ -export const halCommonCrc16 = (newByte: number, prevResult: number): number => { - /* - * 16bit CRC notes: - * "CRC-CCITT" - * poly is g(X) = X^16 + X^12 + X^5 + 1 (0x1021) - * used in the FPGA (green boards and 15.4) - * initial remainder should be 0xFFFF - */ - prevResult = ((prevResult >> 8) & 0xFFFF) | ((prevResult << 8) & 0xFFFF); - prevResult ^= newByte; - prevResult ^= (prevResult & 0xFF) >> 4; - prevResult ^= (((prevResult << 8) & 0xFFFF) << 4) & 0xFFFF; - - prevResult ^= (((prevResult & 0xFF) << 5) & 0xFF) | (((((prevResult & 0xFF) >> 3) & 0xFFFF) << 8) & 0xFFFF); - - return prevResult; -}; - - -//-------------------------------------------------------------- -// Byte manipulation - -/** Returns the low bits of the 8-bit value 'n' as uint8_t. */ -export const lowBits = (n: number): number => (n & 0xF); -/** Returns the high bits of the 8-bit value 'n' as uint8_t. */ -export const highBits = (n: number): number => (lowBits(n >> 4) & 0xF); -/** Returns the low byte of the 16-bit value 'n' as uint8_t. */ -export const lowByte = (n: number): number => (n & 0xFF); -/** Returns the high byte of the 16-bit value 'n' as uint8_t. */ -export const highByte = (n: number): number => (lowByte(n >> 8) & 0xFF); -/** Returns the value built from the two uint8_t values high and low. */ -export const highLowToInt = (high: number, low: number): number => (((high & 0xFFFF) << 8) + ((low & 0xFFFF) & 0xFF)); -/** Useful to reference a single bit of a byte. */ -export const bit = (x: number): number => (1 << x); -/** Useful to reference a single bit of an uint32_t type. */ -export const bit32 = (x: number): number => (1 << x); -/** Returns both the low and high bytes (in that order) of the same 16-bit value 'n' as uint8_t. */ -export const lowHighBytes = (n: number): [number, highByte: number] => [lowByte(n), highByte(n)]; -/** Returns both the low and high bits (in that order) of the same 8-bit value 'n' as uint8_t. */ -export const lowHighBits = (n: number): [number, highBits: number] => [lowBits(n), highBits(n)]; - -/** - * Get byte as an 8-bit string (`n` assumed of proper range). - * @param n - * @returns - */ -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 405d164b8b6..00000000000 --- a/src/adapter/ember/zdo.ts +++ /dev/null @@ -1,1034 +0,0 @@ -//------------------------------------------------------------------------------ -// ZigBee Device Object (ZDO) - -import {EmberEUI64, EmberExtendedPanId, EmberNodeId} from "./types"; - -/** 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: EmberExtendedPanId, - /** - * 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, - /** The 16-bit network address of the neighboring device. 16-bit */ - nodeId: EmberNodeId, - /** - * 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: EmberNodeId, - /** - * 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: EmberNodeId, -}; - -export type ZDOBindingTableEntry = { - /** The source IEEE address for the binding entry. */ - sourceEui64: EmberEUI64, - /** 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: EmberNodeId | EmberEUI64, - /** - * 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: EmberEUI64, - nodeId: EmberNodeId, - assocDevList: number[], -}; - -/** @see NETWORK_ADDRESS_RESPONSE */ -export type NetworkAddressResponsePayload = { - eui64: EmberEUI64, - nodeId: EmberNodeId, - assocDevList: number[], -}; - -/** @see MATCH_DESCRIPTORS_RESPONSE */ -export type MatchDescriptorsResponsePayload = { - nodeId: EmberNodeId, - endpointList: number[], -}; - -/** @see SIMPLE_DESCRIPTOR_RESPONSE */ -export type SimpleDescriptorResponsePayload = { - nodeId: EmberNodeId, - /** 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: EmberNodeId, - logicalType: number, - macCapFlags: MACCapabilityFlags, - manufacturerCode: number, - stackRevision: number, -}; - -/** @see POWER_DESCRIPTOR_RESPONSE */ -export type PowerDescriptorResponsePayload = { - nodeId: EmberNodeId, - currentPowerMode: number, - availPowerSources: number, - currentPowerSource: number, - currentPowerSourceLevel: number, -}; - -/** @see ACTIVE_ENDPOINTS_RESPONSE */ -export type ActiveEndpointsResponsePayload = { - nodeId: EmberNodeId, - 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: EmberNodeId, - eui64: EmberEUI64, - capabilities: MACCapabilityFlags, -}; - -/** - * 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] [child Age:4]* - */ -export const PARENT_ANNOUNCE = 0x001F; -/** - * Response: [transaction sequence number: 1] - * [number of children:1] [child EUI64:8] [child Age:4]* - */ -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; - diff --git a/src/adapter/tstype.ts b/src/adapter/tstype.ts index e66700b892b..42105a91e32 100644 --- a/src/adapter/tstype.ts +++ b/src/adapter/tstype.ts @@ -10,7 +10,7 @@ interface SerialPortOptions { baudRate?: number; rtscts?: boolean; path?: string; - adapter?: 'zstack' | 'deconz' | 'zigate' | 'ezsp' | 'ember' | 'auto'; + adapter?: 'zstack' | 'deconz' | 'zigate' | 'ezsp' | 'auto'; } interface AdapterOptions { diff --git a/src/utils/backup.ts b/src/utils/backup.ts index 973e02177bf..34c259422ca 100644 --- a/src/utils/backup.ts +++ b/src/utils/backup.ts @@ -111,10 +111,6 @@ export const fromUnifiedBackup = (backup: Models.UnifiedBackupStorage): Models.B znp: { version: backup.metadata.internal?.znpVersion || undefined, trustCenterLinkKeySeed: tclkSeedString ? Buffer.from(tclkSeedString, "hex") : undefined, - }, - ezsp: { - version: backup.metadata.internal?.ezspVersion || undefined, - hashed_tclk: backup.stack_specific?.ezsp?.hashed_tclk ? Buffer.from(backup.stack_specific.ezsp.hashed_tclk, "hex") : undefined, } }; }; diff --git a/test/adapter/ember/ash.test.ts b/test/adapter/ember/ash.test.ts deleted file mode 100644 index 3f2f9380fbd..00000000000 --- a/test/adapter/ember/ash.test.ts +++ /dev/null @@ -1,347 +0,0 @@ -import {OpenOptions} from '@serialport/stream' -import {MockBinding, MockBindingInterface} from '@serialport/binding-mock' -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 {EzspStatus} from "../../../src/adapter/ember/enums"; -import {EzspBuffer} from "../../../src/adapter/ember/uart/queues"; -import {UartAsh} from "../../../src/adapter/ember/uart/ash"; -import {EZSP_HOST_RX_POOL_SIZE, TX_POOL_BUFFERS} from "../../../src/adapter/ember/uart/consts"; -import {RECD_RSTACK_BYTES, SEND_RST_BYTES, NCP_ACK_FIRST, adapterSONOFFDongleE} from "./consts"; -import {EzspBuffalo} from "../../../src/adapter/ember/ezsp/buffalo.ts"; -import {lowByte} from '../../../src/adapter/ember/utils/math'; -import {EzspFrameID} from '../../../src/adapter/ember/ezsp/enums.ts'; -import {Wait} from '../../../src/utils/'; - -// XXX: Below are copies from uart>ash.ts, should be kept in sync (avoids export) -/** max frames sent without being ACKed (1-7) */ -const CONFIG_TX_K = 3; -/** enables randomizing DATA frame payloads */ -const CONFIG_RANDOMIZE = true; -/** adaptive rec'd ACK timeout initial value */ -const CONFIG_ACK_TIME_INIT = 800; -/** " " " " " minimum value */ -const CONFIG_ACK_TIME_MIN = 400; -/** " " " " " maximum value */ -const CONFIG_ACK_TIME_MAX = 2400; -/** time allowed to receive RSTACK after ncp is reset */ -const CONFIG_TIME_RST = 2500; -/** time between checks for received RSTACK (CONNECTED status) */ -const CONFIG_TIME_RST_CHECK = 100; -/** if free buffers < limit, host receiver isn't ready, will hold off the ncp from sending normal priority frames */ -const CONFIG_NR_LOW_LIMIT = 8;// RX_FREE_LW -/** if free buffers > limit, host receiver is ready */ -const CONFIG_NR_HIGH_LIMIT = 12;// RX_FREE_HW -/** time until a set nFlag must be resent (max 2032) */ -const CONFIG_NR_TIME = 480; -/** Read/write max bytes count at stream level */ -const CONFIG_HIGHWATER_MARK = 256; - -const mockSerialPortCloseEvent = jest.fn(); -const mockSerialPortErrorEvent = jest.fn(); - -// todo doesnt reset if closing -// todo doesnt start if closing or connected -// todo doesnt close port if already closed on stop -// todo port error triggers stop -// todo emit `reset` only on port error -// todo emit `close` only when ASH layer stopped -// todo emit `frame` only on valid DATA frame - -const mocks = [mockSerialPortCloseEvent, mockSerialPortErrorEvent]; - -describe('Ember UART ASH Protocol', () => { - const openOpts: OpenOptions = {path: '/dev/ttyACM0', baudRate: 115200, binding: MockBinding}; - /** - * Mock binding provides: - * - * uartAsh.serialPort.port.recording => Buffer of all data written if record==true - * - * uartAsh.serialPort.port.lastWrite => Buffer of last write - */ - let uartAsh: UartAsh; - let buffalo: EzspBuffalo; - let frameSequence: number; - - beforeAll(async () => { - jest.useRealTimers();// messes with serialport promise handling otherwise? - }); - afterAll(async () => { - jest.useRealTimers(); - }); - beforeEach(() => { - for (const mock of mocks) { - mock.mockClear(); - } - - frameSequence = 0; - uartAsh = new UartAsh(openOpts); - buffalo = new EzspBuffalo(Buffer.alloc(EZSP_MAX_FRAME_LENGTH)); - MockBinding.createPort('/dev/ttyACM0', {/*echo: true,*/ record: true, /*readyData: emitRSTACK,*/ ...adapterSONOFFDongleE}); - - buffalo.setPosition(0); - }); - afterEach(async () => { - await uartAsh.stop(); - MockBinding.reset(); - }); - - it('Inits properly and allocates buffers as needed', () => { - expect(uartAsh.connected).toStrictEqual(false); - expect(uartAsh.txQueue).toBeDefined(); - expect(uartAsh.reTxQueue).toBeDefined(); - expect(uartAsh.txFree).toBeDefined(); - expect(uartAsh.rxQueue).toBeDefined(); - expect(uartAsh.rxFree).toBeDefined(); - - //@ts-expect-error private - uartAsh.initVariables(); - - expect(uartAsh.ncpSleepEnabled).toStrictEqual(false); - expect(uartAsh.ncpHasCallbacks).toStrictEqual(false); - expect(uartAsh.txQueue.length).toStrictEqual(0); - expect(uartAsh.reTxQueue.length).toStrictEqual(0); - expect(uartAsh.txFree.length).toStrictEqual(TX_POOL_BUFFERS); - expect(uartAsh.rxQueue.length).toStrictEqual(0); - expect(uartAsh.rxFree.length).toStrictEqual(EZSP_HOST_RX_POOL_SIZE); - expect(uartAsh.txQueue.tail).toStrictEqual(null); - expect(uartAsh.reTxQueue.tail).toStrictEqual(null); - expect(uartAsh.txFree.link).toBeInstanceOf(EzspBuffer); - expect(uartAsh.txFree.link.data.length).toStrictEqual(EZSP_MAX_FRAME_LENGTH); - expect(uartAsh.rxQueue.tail).toStrictEqual(null); - expect(uartAsh.rxFree.link).toBeInstanceOf(EzspBuffer); - expect(uartAsh.rxFree.link.data.length).toStrictEqual(EZSP_MAX_FRAME_LENGTH); - - for (const c in uartAsh.counters) { - expect(uartAsh.counters[c]).toStrictEqual(0); - } - - // this is mostly Queues testing, but make sure it works in "real" context - const link = uartAsh.txFree.link; - const buffer: EzspBuffer = uartAsh.txFree.allocBuffer(); - - expect(buffer).toStrictEqual(link); - expect(uartAsh.txFree.link).toStrictEqual(buffer.link); - expect(uartAsh.txFree.length).toStrictEqual(TX_POOL_BUFFERS - 1); - - uartAsh.txQueue.addTail(buffer); - - expect(buffer.link).toStrictEqual(null); - expect(uartAsh.txQueue.tail).toStrictEqual(buffer); - expect(uartAsh.txQueue.length).toStrictEqual(1); - - const head = uartAsh.txQueue.removeHead(); - - expect(head).toStrictEqual(buffer); - expect(head).toStrictEqual(link); - expect(uartAsh.txQueue.tail).toStrictEqual(null); - expect(uartAsh.txQueue.length).toStrictEqual(0); - - uartAsh.txFree.freeBuffer(head); - - uartAsh.txQueue.addTail(uartAsh.txFree.allocBuffer()); - uartAsh.txQueue.addTail(uartAsh.txFree.allocBuffer()); - - expect(uartAsh.txQueue.length).toStrictEqual(2); - expect(uartAsh.txFree.length).toStrictEqual(TX_POOL_BUFFERS - 2); - }); - it('Reaches CONNECTED state', async () => { - //@ts-expect-error private - const initVariablesSpy = jest.spyOn(uartAsh, 'initVariables'); - //@ts-expect-error private - const initPortSpy = jest.spyOn(uartAsh, 'initPort'); - const resetNcpSpy = jest.spyOn(uartAsh, 'resetNcp'); - const sendExecSpy = jest.spyOn(uartAsh, 'sendExec'); - //@ts-expect-error private - const onPortCloseSpy = jest.spyOn(uartAsh, 'onPortClose'); - //@ts-expect-error private - const onPortErrorSpy = jest.spyOn(uartAsh, 'onPortError'); - - const resetResult = (await uartAsh.resetNcp()); - - //@ts-expect-error private - expect(uartAsh.serialPort.settings.binding).toBe(MockBinding);// just making sure mock was registered - expect(resetResult).toStrictEqual(EzspStatus.SUCCESS); - expect(resetNcpSpy).toHaveBeenCalledTimes(1); - expect(initVariablesSpy).toHaveBeenCalledTimes(1); - expect(initPortSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(uartAsh.flags).toStrictEqual(48);// RST|CAN - //@ts-expect-error private - expect(uartAsh.serialPort).toBeDefined(); - //@ts-expect-error private - expect(uartAsh.writer).toBeDefined(); - //@ts-expect-error private - expect(uartAsh.parser).toBeDefined(); - expect(uartAsh.portOpen).toBeTruthy(); - - //@ts-expect-error private - uartAsh.serialPort.port.emitData(Buffer.from(RECD_RSTACK_BYTES)); - const startResult = (await uartAsh.start()); - - expect(startResult).toStrictEqual(EzspStatus.SUCCESS); - expect(sendExecSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(uartAsh.serialPort.port.lastWrite).toStrictEqual(Buffer.from(SEND_RST_BYTES)); - expect(uartAsh.connected).toBeTruthy(); - expect(uartAsh.counters.txAllFrames).toStrictEqual(1);// RST - expect(uartAsh.counters.rxAllFrames).toStrictEqual(1);// RSTACK - - Object.keys(uartAsh.counters).forEach((key) => { - if (key !== 'txAllFrames' && key !== 'rxAllFrames') { - expect(uartAsh.counters[key]).toStrictEqual(0); - } - }); - - await uartAsh.stop(); - - expect(initVariablesSpy).toHaveBeenCalledTimes(2);// always called on stop - expect(onPortErrorSpy).toHaveBeenCalledTimes(0); - expect(onPortCloseSpy).toHaveBeenCalledTimes(1); - }); - it.skip('Resets but failed to start b/c error in RSTACK frame returned by NCP', async () => { - //@ts-expect-error private - const rejectFrameSpy = jest.spyOn(uartAsh, 'rejectFrame'); - //@ts-expect-error private - const receiveFrameSpy = jest.spyOn(uartAsh, 'receiveFrame'); - //@ts-expect-error private - const decodeByteSpy = jest.spyOn(uartAsh, 'decodeByte'); - - const resetResult = (await uartAsh.resetNcp()); - - expect(resetResult).toStrictEqual(EzspStatus.SUCCESS); - - const badCrcRSTACK = Buffer.from(RECD_RSTACK_BYTES); - badCrcRSTACK[badCrcRSTACK.length - 2] = 0;// throw CRC low - - //@ts-expect-error private - uartAsh.serialPort.port.emitData(badCrcRSTACK); - const startResult = (await uartAsh.start()); - - await Wait(10); - - expect(startResult).toStrictEqual(EzspStatus.HOST_FATAL_ERROR); - expect(uartAsh.counters.txAllFrames).toStrictEqual(1); - expect(uartAsh.counters.rxAllFrames).toStrictEqual(0); - expect(uartAsh.counters.rxCrcErrors).toStrictEqual(1); - expect(rejectFrameSpy).toHaveBeenCalledTimes(1);// received bad RSTACK - expect(decodeByteSpy.mock.results[decodeByteSpy.mock.results.length - 1].value[0]).toStrictEqual(EzspStatus.ASH_BAD_CRC); - expect(receiveFrameSpy).toHaveLastReturnedWith(EzspStatus.NO_RX_DATA); - expect(uartAsh.connected).toBeFalsy(); - }); - describe('In CONNECTED state...', () => { - beforeEach(async () => { - const resetResult = (await uartAsh.resetNcp()); - //@ts-expect-error private - uartAsh.serialPort.port.emitData(Buffer.from(RECD_RSTACK_BYTES)); - const startResult = (await uartAsh.start()); - - expect(resetResult).toStrictEqual(EzspStatus.SUCCESS); - expect(startResult).toStrictEqual(EzspStatus.SUCCESS); - expect(uartAsh.connected).toBeTruthy(); - - uartAsh.sendExec();// ACK for RSTACK == 8070787e - expect(uartAsh.idle).toBeTruthy(); - expect(uartAsh.counters.txAckFrames).toStrictEqual(1);// ACK for RSTACK - }); - afterEach(async () => { - }); - - it('Sends DATA frame to NCP', async () => { - buffalo.setPosition(EZSP_PARAMETERS_INDEX); - buffalo.setCommandByte(EZSP_FRAME_ID_INDEX, lowByte(EzspFrameID.VERSION)); - buffalo.setCommandByte(EZSP_SEQUENCE_INDEX, frameSequence++); - buffalo.setCommandByte( - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - (EZSP_FRAME_CONTROL_COMMAND | (0x00 & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - buffalo.writeUInt8(13);// desiredProtocolVersion - - let sendBuf = buffalo.getWritten(); - - uartAsh.send(sendBuf.length, sendBuf); - - await Wait(10); - - expect(uartAsh.counters.txDataFrames).toStrictEqual(1); - //@ts-expect-error private - expect(uartAsh.serialPort.port.recording).toStrictEqual(Buffer.concat([ - Buffer.from('1ac038bc7e', 'hex'),// RST - Buffer.from('8070787e', 'hex'),// RSTACK ACK - Buffer.from('004221a8597c057e', 'hex'),// DATA - ])); - }) - - it('Sends DATA frame and receives response from NCP', async () => { - buffalo.setPosition(EZSP_PARAMETERS_INDEX); - buffalo.setCommandByte(EZSP_FRAME_ID_INDEX, lowByte(EzspFrameID.VERSION)); - buffalo.setCommandByte(EZSP_SEQUENCE_INDEX, frameSequence++); - buffalo.setCommandByte( - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - (EZSP_FRAME_CONTROL_COMMAND | (0x00 & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - buffalo.writeUInt8(2);// desiredProtocolVersion - - let sendBuf = buffalo.getWritten(); - - uartAsh.send(sendBuf.length, sendBuf); - - await Wait(10); - - //@ts-expect-error private - uartAsh.serialPort.port.emitData(Buffer.from(NCP_ACK_FIRST));// just an ACK, doesn't matter what it is - - await Wait(10);// force wait new frame - - expect(uartAsh.counters.txAckFrames).toStrictEqual(1); - expect(uartAsh.counters.rxAckFrames).toStrictEqual(1); - }); - - it('TODO: Sends DATA frame with NR flags when buffers are low on host', async () => {}); - - it('TODO: Sends DATA frame but times out waiting for response', async () => {}); - - it('TODO: Resends DATA frame', async () => {}); - - it('Allows sending up to TX_K frames before receiving ACK', async () => { - buffalo.setPosition(EZSP_PARAMETERS_INDEX); - buffalo.setCommandByte(EZSP_FRAME_ID_INDEX, lowByte(EzspFrameID.VERSION)); - buffalo.setCommandByte(EZSP_SEQUENCE_INDEX, frameSequence++); - buffalo.setCommandByte( - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - (EZSP_FRAME_CONTROL_COMMAND | (0x00 & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - buffalo.writeUInt8(13);// desiredProtocolVersion - - let sendBuf = buffalo.getWritten(); - - for (let i = 0; i <= CONFIG_TX_K; i++) { - uartAsh.send(sendBuf.length, sendBuf); - } - - await Wait(10); - - expect(uartAsh.counters.txDataFrames).toStrictEqual(3); - expect(uartAsh.txQueue.length).toStrictEqual(1); - - //@ts-expect-error private - expect(uartAsh.serialPort.port.recording).toStrictEqual(Buffer.concat([ - Buffer.from('1ac038bc7e', 'hex'),// RST - Buffer.from('8070787e', 'hex'),// RSTACK ACK - Buffer.from('004221a8597c057e', 'hex'),// DATA 1 - Buffer.from('104221a859785f7e', 'hex'),// DATA 2 - Buffer.from('204221a85974b17e', 'hex'),// DATA 3 - ])); - }); - }); -}); diff --git a/test/adapter/ember/consts.ts b/test/adapter/ember/consts.ts deleted file mode 100644 index a5686dc0c93..00000000000 --- a/test/adapter/ember/consts.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {ASH_VERSION} from "../../../src/adapter/ember/uart/consts"; -import {AshFrameType, AshReservedByte, NcpFailedCode} from "../../../src/adapter/ember/uart/enums"; - - -export const adapterSONOFFDongleE = {manufacturer: 'ITEAD', vendorId: '1a86', productId: '55d4'}; -export const adapterHASkyConnect = {manufacturer: 'Nabu Casa', vendorId: '10c4', productId: 'ea60'}; - -/** - * Bytes sent to NCP on init - * - * 1ac038bc7e - */ -export const SEND_RST_BYTES = [ - AshReservedByte.CANCEL,// 26 - 0x1a - AshFrameType.RST,// 192 - 0xc0 - 56,// CRC high - 0x38 - 188,// CRC low - 0xbc - AshReservedByte.FLAG,// 126 - 0x7e -]; -/** - * Pre-decoding values. - * - * [193, 2, 2, 155, 123, 126] - * - * 1ac1020b0a527e - */ -export const RECD_RSTACK_BYTES = [ - AshReservedByte.CANCEL,// 26 - 0x1a - AshFrameType.RSTACK,// 193 - 0xc1 - ASH_VERSION,// 2 - 0x02 - NcpFailedCode.RESET_SOFTWARE,// 11 - 0x0b - 10,// CRC high - 0x0a - 82,// CRC low - 0x52 - AshReservedByte.FLAG,// 126 - 0x7e -]; -/** - * ACK sent by NCP after first DATA frame received. - * - * ACK(1)+ - */ -export const NCP_ACK_FIRST = [ - AshFrameType.ACK + 1, - 0x60,// CRC High - 0x59,// CRC Low - AshReservedByte.FLAG -]; diff --git a/test/adapter/ember/ezspBuffalo.test.ts b/test/adapter/ember/ezspBuffalo.test.ts deleted file mode 100644 index 29cd22abf90..00000000000 --- a/test/adapter/ember/ezspBuffalo.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -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'; -import {lowByte} from '../../../src/adapter/ember/utils/math'; - - -describe('Ember EZSP Buffalo', () => { - let buffalo: EzspBuffalo; - - beforeAll(async () => { - }); - - afterAll(async () => { - }); - - beforeEach(() => { - buffalo = new EzspBuffalo(Buffer.alloc(EZSP_MAX_FRAME_LENGTH)); - }); - - afterEach(() => { - }); - - it('Is empty after init', () => { - expect(buffalo.getWritten()).toStrictEqual(Buffer.from([])); - }); - - it('Writes & read at position without altering internal position tracker', () => { - // mock send `version` command logic flow - buffalo.setPosition(EZSP_PARAMETERS_INDEX); - buffalo.setCommandByte(EZSP_FRAME_ID_INDEX, lowByte(EzspFrameID.VERSION)); - buffalo.setCommandByte(EZSP_SEQUENCE_INDEX, 0); - buffalo.setCommandByte( - EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX, - (EZSP_FRAME_CONTROL_COMMAND | (0x00 & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - buffalo.writeUInt8(12);// desiredProtocolVersion - - expect(buffalo.getWritten()).toStrictEqual(Buffer.from([0x00, 0x00, 0x00, 0x0c])); - - expect(buffalo.getCommandByte(EZSP_FRAME_ID_INDEX)).toStrictEqual(lowByte(EzspFrameID.VERSION)); - expect(buffalo.getCommandByte(EZSP_SEQUENCE_INDEX)).toStrictEqual(0); - expect(buffalo.getCommandByte(EZSP_EXTENDED_FRAME_CONTROL_LB_INDEX)).toStrictEqual( - (EZSP_FRAME_CONTROL_COMMAND | (0x00 & EZSP_FRAME_CONTROL_SLEEP_MODE_MASK) - | ((0x00 << EZSP_FRAME_CONTROL_NETWORK_INDEX_OFFSET) & EZSP_FRAME_CONTROL_NETWORK_INDEX_MASK)) - ); - }); -}); diff --git a/test/adapter/ember/math.test.ts b/test/adapter/ember/math.test.ts deleted file mode 100644 index dc68268e842..00000000000 --- a/test/adapter/ember/math.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as m from "../../../src/adapter/ember/utils/math"; - -const ASH_CRC_INIT = 0xFFFF; -const B32 = 0xBEEFFACE; - -describe('Ember Math utils', () => { - it('mod8', () => { - let t = m.mod8(0x00); - expect(t).toStrictEqual(0); - t = m.mod8(0x03); - expect(t).toStrictEqual(3); - t = m.mod8(0x07); - expect(t).toStrictEqual(7); - t = m.mod8(0x08); - expect(t).toStrictEqual(0); - t = m.mod8(0x10); - expect(t).toStrictEqual(0); - t = m.mod8(0xFE); - expect(t).toStrictEqual(6); - t = m.mod8(0xFF); - expect(t).toStrictEqual(7); - }); - it('inc8', () => { - let t = m.inc8(0x00); - expect(t).toStrictEqual(1); - t = m.inc8(0x03); - expect(t).toStrictEqual(4); - t = m.inc8(0x07); - expect(t).toStrictEqual(0); - t = m.inc8(0x08); - expect(t).toStrictEqual(1); - t = m.inc8(0x10); - expect(t).toStrictEqual(1); - t = m.inc8(0xFE); - expect(t).toStrictEqual(7); - t = m.inc8(0xFF); - expect(t).toStrictEqual(0); - }); - it('withinRange', () => { - let t = m.withinRange(0x00, 0x04, 0x07); - expect(t).toStrictEqual(true); - t = m.withinRange(0x00, 0x04, 0x08); - expect(t).toStrictEqual(false); - t = m.withinRange(0xAA, 0xAC, 0xFE); - expect(t).toStrictEqual(true); - t = m.withinRange(0x00, 0x04, 0xF8); - expect(t).toStrictEqual(false); - }); - it('halCommonCrc16', () => { - let t = m.halCommonCrc16(0x00, ASH_CRC_INIT); - expect(t).toStrictEqual(57840); - t = m.halCommonCrc16(0x03, t); - expect(t).toStrictEqual(11628); - t = m.halCommonCrc16(0xFE, t); - expect(t).toStrictEqual(38686); - t = m.halCommonCrc16(0xA5, t); - expect(t).toStrictEqual(2065); - }); - it('lowBits', () => { - let t = m.lowBits(10); - expect(t).toStrictEqual(10); - t = m.lowBits(100); - expect(t).toStrictEqual(4); - }); - it('highBits', () => { - let t = m.highBits(10); - expect(t).toStrictEqual(0); - t = m.highBits(100); - expect(t).toStrictEqual(6); - }); - it('lowByte', () => { - let t = m.lowByte(10); - expect(t).toStrictEqual(10); - t = m.lowByte(100); - expect(t).toStrictEqual(100); - t = m.lowByte(1000); - expect(t).toStrictEqual(232); - t = m.lowByte(255); - expect(t).toStrictEqual(255); - t = m.lowByte(1024); - expect(t).toStrictEqual(0); - t = m.lowByte(B32); - expect(t).toStrictEqual(206); - }); - it('highByte', () => { - let t = m.highByte(10); - expect(t).toStrictEqual(0); - t = m.highByte(100); - expect(t).toStrictEqual(0); - t = m.highByte(1000); - expect(t).toStrictEqual(3); - t = m.highByte(255); - expect(t).toStrictEqual(0); - t = m.highByte(1024); - expect(t).toStrictEqual(4); - t = m.highByte(B32); - expect(t).toStrictEqual(250); - }); - it('highLowToInt', () => { - let t = m.highLowToInt(254, 10); - expect(t).toStrictEqual(65034); - t = m.highLowToInt(10, 100); - expect(t).toStrictEqual(2660); - t = m.highLowToInt(1000, 2000); - expect(t).toStrictEqual(256208); - t = m.highLowToInt(355, 255); - expect(t).toStrictEqual(91135); - t = m.highLowToInt(123, 1024); - expect(t).toStrictEqual(31488); - t = m.highLowToInt(1, B32); - expect(t).toStrictEqual(462); - t = m.highLowToInt(B32, 1); - expect(t).toStrictEqual(16436737); - }); - it('bit', () => { - let t = m.bit(11); - expect(t).toStrictEqual(2048); - t = m.bit(15); - expect(t).toStrictEqual(32768); - t = m.bit(26); - expect(t).toStrictEqual(67108864); - t = (m.bit(11) | m.bit(15) | m.bit(20) | m.bit(25)); - expect(t).toStrictEqual(34637824); - t = ((m.bit(12) | m.bit(13) | m.bit(14) | m.bit(16) | m.bit(17) | m.bit(18) | - m.bit(19) | m.bit(21) | m.bit(22) | m.bit(23) | m.bit(24) | m.bit(26))); - expect(t).toStrictEqual(99577856); - t = 53 & m.bit(0); - expect(t).toStrictEqual(1); - t = 53 & m.bit(3); - expect(t).toStrictEqual(0); - t = 53 | m.bit(0); - expect(t).toStrictEqual(53); - t = 53 | m.bit(3); - expect(t).toStrictEqual(61); - }); - it('bit32', () => { - let t = m.bit32(11); - expect(t).toStrictEqual(2048); - t = m.bit32(15); - expect(t).toStrictEqual(32768); - t = m.bit32(26); - expect(t).toStrictEqual(67108864); - t = (m.bit32(11) | m.bit32(15) | m.bit32(20) | m.bit32(25)); - expect(t).toStrictEqual(34637824); - t = ((m.bit32(12) | m.bit32(13) | m.bit32(14) | m.bit32(16) | m.bit32(17) | m.bit32(18) | - m.bit32(19) | m.bit32(21) | m.bit32(22) | m.bit32(23) | m.bit32(24) | m.bit32(26))); - expect(t).toStrictEqual(99577856); - t = B32 & m.bit32(0); - expect(t).toStrictEqual(0); - t = B32 & m.bit32(3); - expect(t).toStrictEqual(8); - t = B32 | m.bit32(0); - expect(t).toStrictEqual(-1091568945); - t = B32 | m.bit32(3); - expect(t).toStrictEqual(-1091568946); - }); - it('lowHighBytes', () => { - let [l, h] = m.lowHighBytes(1024); - expect(l).toStrictEqual(0); - expect(h).toStrictEqual(4); - expect(l).toStrictEqual(m.lowByte(1024)); - expect(h).toStrictEqual(m.highByte(1024)); - [l, h] = m.lowHighBytes(255); - expect(l).toStrictEqual(255); - expect(h).toStrictEqual(0); - }); - it('lowHighBits', () => { - let [l, h] = m.lowHighBits(10); - expect(l).toStrictEqual(10); - expect(h).toStrictEqual(0); - expect(l).toStrictEqual(m.lowBits(10)); - expect(h).toStrictEqual(m.highBits(0)); - [l, h] = m.lowHighBits(100); - expect(l).toStrictEqual(4); - expect(h).toStrictEqual(6); - }); - it('byteToBits', () => { - let t = m.byteToBits(2); - expect(t).toStrictEqual('00000010'); - t = m.byteToBits(4); - expect(t).toStrictEqual('00000100'); - }) -}); diff --git a/test/adapter/ember/requestQueue.test.ts b/test/adapter/ember/requestQueue.test.ts deleted file mode 100644 index 2017968b95d..00000000000 --- a/test/adapter/ember/requestQueue.test.ts +++ /dev/null @@ -1,613 +0,0 @@ -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 {Wait} from '../../../src/utils'; - -let fakeWaitTime = 1000; -let varyingReturn: EmberStatus = EmberStatus.SUCCESS; -const getVaryingReturn = async (): Promise => { - await Wait(fakeWaitTime); - return varyingReturn; -}; -const getThrownError = async (): Promise => { - await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.ASH_ACK_TIMEOUT]); -} -const getThrowNetworkBusy = async (): Promise => { - await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.NO_TX_SPACE]); -}; -const getThrowNetworkDown = async (): Promise => { - await Wait(fakeWaitTime); - throw new Error(EzspStatus[EzspStatus.NOT_CONNECTED]); -}; - -class TestThis { - public bs: boolean; - public q: EmberRequestQueue; - - constructor() { - this.bs = false; - this.q = new EmberRequestQueue(60); - } - - public async getNewBS(): Promise { - await new Promise((resolve, reject): void => { - this.q.enqueue( - async (): Promise => { - await Wait(fakeWaitTime); - - this.bs = true; - - resolve(); - return EmberStatus.SUCCESS; - }, - reject, - ) - }) - - return this.bs; - } -} - -let deferSpy; - -describe('Ember Request Queue', () => { - let requestQueue: EmberRequestQueue; - - beforeAll(async () => { - jest.useFakeTimers(); - }); - - afterAll(async () => { - jest.useRealTimers(); - }); - - beforeEach(() => { - requestQueue = new EmberRequestQueue(60); - // don't let defer (dispatching tick mgmt) interfere with "manual" flow of tests unless wanted - deferSpy = jest.spyOn(requestQueue, 'defer').mockImplementation(jest.fn()); - }); - - afterEach(() => { - fakeWaitTime = 1000; - varyingReturn = EmberStatus.SUCCESS; - }); - - it('Queues request and resolves it', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.SUCCESS; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - 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'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(p).resolves.toBe(123);// gives result of resolve - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request, rejects it on error, and removes it from queue', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.ERR_FATAL; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - 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'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(p).rejects.toStrictEqual(new Error(EmberStatus[varyingReturn])); - - expect(funcSpy).toHaveBeenCalledTimes(1); - expect(funcRejectSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in 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: EmberStatus = await getThrownError(); - - if (status !== EmberStatus.SUCCESS) { - 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'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(p).rejects.toStrictEqual(new Error(EzspStatus[EzspStatus.ASH_ACK_TIMEOUT])); - - expect(funcSpy).toHaveBeenCalledTimes(1); - expect(funcRejectSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request, defers on NETWORK_BUSY and defers again on NETWORK_DOWN', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.NETWORK_BUSY; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// enqueued - - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100 + (NETWORK_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 + 100); - - varyingReturn = EmberStatus.NETWORK_DOWN; - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100 + (NETWORK_DOWN_DEFER_MSEC * 0.25)); - - expect(deferSpy).toHaveBeenCalledTimes(2); - expect(funcSpy).toHaveBeenCalledTimes(2);// dispatch x2, func called x2 - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// still in queue - - await jest.advanceTimersByTimeAsync(NETWORK_DOWN_DEFER_MSEC + 100); - }); - it('Queues request, defers on NETWORK_BUSY and then resolves it', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.NETWORK_BUSY; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// enqueued - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100 + (NETWORK_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 + 100); - - varyingReturn = EmberStatus.SUCCESS; - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - await expect(p).resolves.toBe(123);// gives result of resolve - - expect(funcSpy).toHaveBeenCalledTimes(2);// enqueued func was called - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request, defers on NETWORK_BUSY and only retries once after internal change', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.NETWORK_BUSY; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - // internally changes external parameter that changes the queue's next run - varyingReturn = EmberStatus.SUCCESS; - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// enqueued - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100 + (NETWORK_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 + 100); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - await expect(p).resolves.toBe(123);// gives result of resolve - - expect(funcSpy).toHaveBeenCalledTimes(2);// enqueued func was called - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request, defers on thrown NETWORK_BUSY', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getThrowNetworkBusy(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// enqueued - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100 + (NETWORK_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 + 100); - }); - it('Queues request and resolves by priority', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.SUCCESS; - const p = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - resolve(123); - return status; - }, - reject, - ); - }); - const pPrio = new Promise((resolve, reject) => { - requestQueue.enqueue( - async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - resolve(456); - return status; - }, - reject, - true, - ); - }); - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcPrioSpy = jest.spyOn(requestQueue.priorityQueue[0], 'func'); - - expect(enqueueSpy).toHaveBeenCalledTimes(2); - expect(funcSpy).toHaveBeenCalledTimes(0); - expect(funcPrioSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - //@ts-expect-error private - expect(requestQueue.priorityQueue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(pPrio).resolves.toBe(456);// gives result of resolve - - expect(funcSpy).toHaveBeenCalledTimes(0);// enqueued func was not called - expect(funcPrioSpy).toHaveBeenCalledTimes(1);// enqueued func was called - //@ts-expect-error private - expect(requestQueue.priorityQueue).toHaveLength(0);// no longer in queue - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// still in queue - - requestQueue.dispatch();// don't await so we can advance timer - - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(p).resolves.toBe(123);// gives result of resolve - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request send & forget style', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.SUCCESS; - - requestQueue.enqueue(async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - return status; - }, () => {}); - - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request send & forget style that errors out silently', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - varyingReturn = EmberStatus.ERR_FATAL; - - requestQueue.enqueue(async (): Promise => { - const status: EmberStatus = await getVaryingReturn(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - return status; - }, () => {}); - - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request send & forget style that throws silently', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - requestQueue.enqueue(async (): Promise => { - const status: EmberStatus = await getThrownError(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - return status; - }, () => {}); - - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(1); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(0);// no longer in queue - }); - it('Queues request send & forget style that throws NETWORK_DOWN silently and retries', async () => { - const enqueueSpy = jest.spyOn(requestQueue, 'enqueue'); - - requestQueue.enqueue(async (): Promise => { - const status: EmberStatus = await getThrowNetworkDown(); - - if (status !== EmberStatus.SUCCESS) { - return status; - } - - return status; - }, () => {}); - - //@ts-expect-error private - const funcSpy = jest.spyOn(requestQueue.queue[0], 'func'); - //@ts-expect-error private - const funcRejectSpy = jest.spyOn(requestQueue.queue[0], 'reject'); - - expect(enqueueSpy).toHaveBeenCalledTimes(1); - expect(funcSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - expect(funcSpy).toHaveBeenCalledTimes(1);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// still in queue - - await jest.advanceTimersByTimeAsync(NETWORK_DOWN_DEFER_MSEC * 2); - - requestQueue.dispatch();// don't await so we can advance timer - - await jest.advanceTimersByTimeAsync(fakeWaitTime + 100); - - expect(funcSpy).toHaveBeenCalledTimes(2);// enqueued func was called - expect(funcRejectSpy).toHaveBeenCalledTimes(0); - //@ts-expect-error private - expect(requestQueue.queue).toHaveLength(1);// still in queue - - await jest.advanceTimersByTimeAsync(NETWORK_DOWN_DEFER_MSEC * 2); - }); - - it('In-class queues also work, just for kicks...', async () => { - const t = new TestThis(); - - expect(t.bs).toBeFalsy(); - - const tBS = t.getNewBS(); - - t.q.dispatch();// don't await so we can advance timer - jest.advanceTimersByTime(fakeWaitTime + 100); - - await expect(tBS).resolves.toBeTruthy(); - }) -}); diff --git a/test/controller.test.ts b/test/controller.test.ts index 0f785ef6d78..36a7717d8ba 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -3717,7 +3717,7 @@ describe('Controller', () => { mockDeconzAdapterAutoDetectPath.mockReturnValueOnce('/dev/test'); let error; try {await Adapter.create(null, {path: null, baudRate: 100, rtscts: false, adapter: 'efr'}, null, null)} catch (e) {error = e;} - expect(error).toStrictEqual(new Error(`Adapter 'efr' does not exists, possible options: zstack, deconz, zigate, ezsp, ember`)); + expect(error).toStrictEqual(new Error(`Adapter 'efr' does not exists, possible options: zstack, deconz, zigate, ezsp`)); }); it('Emit read from device', async () => {