From 53d5a042ea3c927f767ae73bdc48b08208f2a3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Fri, 8 Jan 2021 20:58:37 +0300 Subject: [PATCH 01/63] blank ezsp adapter --- src/adapter/adapter.ts | 7 +- src/adapter/ezsp/adapter/ezspAdapter.ts | 219 +++ src/adapter/ezsp/adapter/index.ts | 7 + src/adapter/ezsp/driver/commands.ts | 681 ++++++++ src/adapter/ezsp/driver/driver.ts | 249 +++ src/adapter/ezsp/driver/ezsp.ts | 214 +++ src/adapter/ezsp/driver/index.ts | 3 + src/adapter/ezsp/driver/test.ts | 37 + src/adapter/ezsp/driver/types/basic.ts | 167 ++ src/adapter/ezsp/driver/types/index.ts | 82 + src/adapter/ezsp/driver/types/named.ts | 1647 +++++++++++++++++++ src/adapter/ezsp/driver/types/struct.ts | 588 +++++++ src/adapter/ezsp/driver/uart.ts | 384 +++++ src/adapter/ezsp/driver/utils/crc16ccitt.ts | 71 + src/adapter/ezsp/driver/utils/index.ts | 133 ++ 15 files changed, 4487 insertions(+), 2 deletions(-) create mode 100644 src/adapter/ezsp/adapter/ezspAdapter.ts create mode 100644 src/adapter/ezsp/adapter/index.ts create mode 100644 src/adapter/ezsp/driver/commands.ts create mode 100644 src/adapter/ezsp/driver/driver.ts create mode 100644 src/adapter/ezsp/driver/ezsp.ts create mode 100644 src/adapter/ezsp/driver/index.ts create mode 100644 src/adapter/ezsp/driver/test.ts create mode 100644 src/adapter/ezsp/driver/types/basic.ts create mode 100644 src/adapter/ezsp/driver/types/index.ts create mode 100644 src/adapter/ezsp/driver/types/named.ts create mode 100644 src/adapter/ezsp/driver/types/struct.ts create mode 100644 src/adapter/ezsp/driver/uart.ts create mode 100644 src/adapter/ezsp/driver/utils/crc16ccitt.ts create mode 100644 src/adapter/ezsp/driver/utils/index.ts diff --git a/src/adapter/adapter.ts b/src/adapter/adapter.ts index a84e03a004..3e5a2a7882 100644 --- a/src/adapter/adapter.ts +++ b/src/adapter/adapter.ts @@ -37,10 +37,13 @@ abstract class Adapter extends events.EventEmitter { const {ZStackAdapter} = await import('./z-stack/adapter'); const {DeconzAdapter} = await import('./deconz/adapter'); const {ZiGateAdapter} = await import('./zigate/adapter'); - type AdapterImplementation = typeof ZStackAdapter | typeof DeconzAdapter | typeof ZiGateAdapter; + const {EZSPAdapter} = await import('./ezsp/adapter'); + type AdapterImplementation = (typeof ZStackAdapter | typeof DeconzAdapter | typeof ZiGateAdapter + | typeof EZSPAdapter); let adapters: AdapterImplementation[]; - const adapterLookup = {zstack: ZStackAdapter, deconz: DeconzAdapter, zigate: ZiGateAdapter}; + const adapterLookup = {zstack: ZStackAdapter, deconz: DeconzAdapter, zigate: ZiGateAdapter, + ezsp: EZSPAdapter}; if (serialPortOptions.adapter) { if (adapterLookup.hasOwnProperty(serialPortOptions.adapter)) { adapters = [adapterLookup[serialPortOptions.adapter]]; diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts new file mode 100644 index 0000000000..ba0bb55dac --- /dev/null +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -0,0 +1,219 @@ +/* istanbul ignore file */ +/* eslint-disable */ +import { + NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, + DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, + StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, +} from '../../tstype'; +import Debug from "debug"; +import Adapter from '../../adapter'; +const debug = Debug("zigbee-herdsman:ezsp:adapter"); +import {Ezsp, Driver} from '../driver'; +import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; +import * as Events from '../../events'; +import * as Zcl from '../../../zcl'; +import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; +import {Queue, Waitress, Wait} from '../../../utils'; + + +interface WaitressMatcher { + address: number | string; + endpoint: number; + transactionSequenceNumber?: number; + frameType: FrameType; + clusterID: number; + commandIdentifier: number; + direction: number; +} + +class EZSPAdapter extends Adapter { + private driver: Driver; + private port: SerialPortOptions; + + public constructor(networkOptions: NetworkOptions, + serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { + super(networkOptions, serialPortOptions, backupPath, adapterOptions); + this.port = serialPortOptions; + this.driver = new Driver(); + } + + /** + * Adapter methods + */ + public async start(): Promise { + await this.driver.startup(this.port.path, {}, debug); + return "resumed"; + } + + public async stop(): Promise { + await this.driver.stop(); + } + + public static async isValidPath(path: string): Promise { + // todo + return true; + } + + public static async autoDetectPath(): Promise { + // todo + return ''; + } + + public async getCoordinator(): Promise { + // todo + return { + networkAddress: 0x0000, + manufacturerID: 0x1135, + ieeeAddr: '', + endpoints: [], + }; + } + + public async permitJoin(seconds: number, networkAddress: number): Promise { + // todo + } + + public async getCoordinatorVersion(): Promise { + // todo + return {type: '', meta: {}}; + } + + public async reset(type: 'soft' | 'hard'): Promise { + return Promise.reject(); + } + + public async supportsLED(): Promise { + return false; + } + + public async setLED(enabled: boolean): Promise { + return Promise.reject(); + } + + public async lqi(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async routingTable(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async nodeDescriptor(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async activeEndpoints(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { + // todo + return Promise.reject(); + } + + public waitFor( + networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, + transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, + ): {promise: Promise; cancel: () => void} { + // todo + return {cancel: undefined, promise: undefined}; + } + + public async sendZclFrameToEndpoint( + ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, + disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, + ): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { + // todo + return Promise.reject(); + } + + public async bind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint?: number + ): Promise { + // todo + return Promise.reject(); + } + + public async unbind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint: number + ): Promise { + // todo + return Promise.reject(); + } + + public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { + // todo + return Promise.reject(); + } + + public async getNetworkParameters(): Promise { + // todo + return Promise.reject(); + } + + public async supportsBackup(): Promise { + //todo + return false; + } + + public async backup(): Promise { + // todo + return Promise.reject(); + } + + public async restoreChannelInterPAN(): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcast( + zclFrame: ZclFrame, timeout: number + ): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcastWithResponse( + zclFrame: ZclFrame, timeout: number + ): Promise { + throw new Error("not supported"); + } + + public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { + throw new Error("not supported"); + } + + public async setTransmitPower(value: number): Promise { + // todo + } + + public async setChannelInterPAN(channel: number): Promise { + //todo + } +} + + +export default EZSPAdapter; \ No newline at end of file diff --git a/src/adapter/ezsp/adapter/index.ts b/src/adapter/ezsp/adapter/index.ts new file mode 100644 index 0000000000..7be4f6b70f --- /dev/null +++ b/src/adapter/ezsp/adapter/index.ts @@ -0,0 +1,7 @@ +/* istanbul ignore file */ +/* eslint-disable */ +import EZSPAdapter from './ezspAdapter'; + +export { + EZSPAdapter, +}; diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts new file mode 100644 index 0000000000..b89c4793f9 --- /dev/null +++ b/src/adapter/ezsp/driver/commands.ts @@ -0,0 +1,681 @@ +import { array } from 'zigbee-herdsman/node_modules/@types/yargs'; +import {/* Basic Types */ + int8s, + uint8_t, + uint16_t, + uint32_t, + LVBytes, + fixed_list, + + /* Named Types */ + EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberNodeId, + EmberPanId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, + EmberGpKeyType, SecureEzspRandomNumber, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, + EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, + EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, + EzspNetworkScanType, EmberJoinDecision, EmberKeyType, + EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, + EzspZllNetworkOperation, + + /* Structs */ + EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, + EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, + EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, + EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, + EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry +} from './types'; + +export const COMMANDS = { + "version": [0, [uint8_t], + [uint8_t, uint8_t, uint16_t] + ], + "getConfigurationValue": [82, [EzspConfigId], + [EzspStatus, uint16_t] + ], + "setConfigurationValue": [83, [EzspConfigId, uint16_t], + [EzspStatus] + ], + "setPolicy": [85, [EzspPolicyId, EzspDecisionId], + [EzspStatus] + ], + "getPolicy": [86, [EzspPolicyId], + [EzspStatus, EzspDecisionId] + ], + "getValue": [170, [EzspValueId], + [EzspStatus, LVBytes] + ], + "getExtendedValue": [3, [EzspExtendedValueId, uint32_t], + [EzspStatus, LVBytes] + ], + "setValue": [171, [EzspValueId, LVBytes], + [EzspStatus] + ], + "setGpioCurrentConfiguration": [172, [uint8_t, uint8_t, uint8_t], + [EzspStatus] + ], + "setGpioPowerUpDownConfiguration": [173, [uint8_t, uint8_t, uint8_t, uint8_t, uint8_t], + [EzspStatus] + ], + "setGpioRadioPowerMask": [174, [uint32_t], + [] + ], + "setCtune": [245, [uint16_t], + [] + ], + "getCtune": [246, [], + [uint16_t] + ], + "setChannelMap": [247, [uint8_t, uint8_t], + [] + ], + "nop": [5, Array(), + [] + ], + "echo": [129, [LVBytes], + [LVBytes] + ], + "invalidCommand": [88, [], + [EzspStatus] + ], + "callback": [6, Array(), + [] + ], + "noCallbacks": [7, Array(), + [] + ], + "setToken": [9, [uint8_t, fixed_list(8, uint8_t)], + [EmberStatus] + ], + "getToken": [10, [uint8_t], + [EmberStatus, fixed_list(8, uint8_t)] + ], + "getMfgToken": [11, [EzspMfgTokenId], + [LVBytes] + ], + "setMfgToken": [12, [EzspMfgTokenId, LVBytes], + [EmberStatus] + ], + "stackTokenChangedHandler": [13, [], + [uint16_t] + ], + "getRandomNumber": [73, [], + [EmberStatus, uint16_t] + ], + "setTimer": [14, [uint8_t, uint16_t, EmberEventUnits, Bool], + [EmberStatus] + ], + "getTimer": [78, [uint8_t], + [uint16_t, EmberEventUnits, Bool] + ], + "timerHandler": [15, [], + [uint8_t] + ], + "debugWrite": [18, [Bool, LVBytes], + [EmberStatus] + ], + "readAndClearCounters": [101, [], + [fixed_list(EmberCounterType.COUNTER_TYPE_COUNT, uint16_t)] + ], + "readCounters": [241, [], + [fixed_list(EmberCounterType.COUNTER_TYPE_COUNT, uint16_t)] + ], + "counterRolloverHandler": [242, [], + [EmberCounterType] + ], + "delayTest": [157, [uint16_t], + [] + ], + "getLibraryStatus": [1, [uint8_t], + [EmberLibraryStatus] + ], + "getXncpInfo": [19, [], + [EmberStatus, uint16_t, uint16_t] + ], + "customFrame": [71, [LVBytes], + [EmberStatus, LVBytes] + ], + "customFrameHandler": [84, [], + [LVBytes] + ], + "getEui64": [38, [], + [EmberEUI64] + ], + "getNodeId": [39, [], + [EmberNodeId] + ], + "networkInit": [23, [], + [EmberStatus] + ], + "setManufacturerCode": [21, [uint16_t], + [] + ], + "setPowerDescriptor": [22, [uint16_t], + [] + ], + "networkInitExtended": [112, [EmberNetworkInitStruct], + [EmberStatus] + ], + "networkState": [24, [], + [EmberNetworkStatus] + ], + "stackStatusHandler": [25, [], + [EmberStatus] + ], + "startScan": [26, [EzspNetworkScanType, uint32_t, uint8_t], + [EmberStatus] + ], + "energyScanResultHandler": [72, [], + [uint8_t, int8s] + ], + "networkFoundHandler": [27, [], + [EmberZigbeeNetwork, uint8_t, int8s] + ], + "scanCompleteHandler": [28, [], + [uint8_t, EmberStatus] + ], + "stopScan": [29, [], + [EmberStatus] + ], + "formNetwork": [30, [EmberNetworkParameters], + [EmberStatus] + ], + "joinNetwork": [31, [EmberNodeType, EmberNetworkParameters], + [EmberStatus] + ], + "leaveNetwork": [32, [], + [EmberStatus] + ], + "findAndRejoinNetwork": [33, [Bool, uint32_t], + [EmberStatus] + ], + "permitJoining": [34, [uint8_t], + [EmberStatus] + ], + "childJoinHandler": [35, [], + [uint8_t, Bool, EmberNodeId, EmberEUI64, EmberNodeType] + ], + "energyScanRequest": [156, [EmberNodeId, uint32_t, uint8_t, uint16_t], + [EmberStatus] + ], + "getNetworkParameters": [40, [], + [EmberStatus, EmberNodeType, EmberNetworkParameters] + ], + "getParentChildParameters": [41, [], + [uint8_t, EmberEUI64, EmberNodeId] + ], + "getChildData": [74, [uint8_t], + [EmberStatus, EmberNodeId, EmberEUI64, EmberNodeType] + ], + "getNeighbor": [121, [uint8_t], + [EmberStatus, EmberNeighborTableEntry] + ], + "neighborCount": [122, [], + [uint8_t] + ], + "getRouteTableEntry": [123, [uint8_t], + [EmberStatus, EmberRouteTableEntry] + ], + "setRadioPower": [153, [int8s], + [EmberStatus] + ], + "setRadioChannel": [154, [uint8_t], + [EmberStatus] + ], + "setConcentrator": [16, [Bool, uint16_t, uint16_t, uint16_t, uint8_t, uint8_t, uint8_t], + [EmberStatus] + ], + "clearBindingTable": [42, [], + [EmberStatus] + ], + "setBinding": [43, [uint8_t, EmberBindingTableEntry], + [EmberStatus] + ], + "getBinding": [44, [uint8_t], + [EmberStatus, EmberBindingTableEntry] + ], + "deleteBinding": [45, [uint8_t], + [EmberStatus] + ], + "bindingIsActive": [46, [uint8_t], + [Bool] + ], + "getBindingRemoteNodeId": [47, [uint8_t], + [EmberNodeId] + ], + "setBindingRemoteNodeId": [48, [uint8_t], + [] + ], + "remoteSetBindingHandler": [49, [], + [EmberBindingTableEntry] + ], + "remoteDeleteBindingHandler": [50, [], + [uint8_t, EmberStatus] + ], + "maximumPayloadLength": [51, [], + [uint8_t] + ], + "sendUnicast": [52, [EmberOutgoingMessageType, EmberNodeId, EmberApsFrame, uint8_t, LVBytes], + [EmberStatus, uint8_t] + ], + "sendBroadcast": [54, [EmberNodeId, EmberApsFrame, uint8_t, uint8_t, LVBytes], + [EmberStatus, uint8_t] + ], + "proxyBroadcast": [55, [EmberNodeId, EmberNodeId, uint8_t, EmberApsFrame, uint8_t, uint8_t, LVBytes], + [EmberStatus, uint8_t] + ], + "sendMulticast": [56, [EmberApsFrame, uint8_t, uint8_t, uint8_t, LVBytes], + [EmberStatus, uint8_t] + ], + "sendReply": [57, [EmberNodeId, EmberApsFrame, LVBytes], + [EmberStatus] + ], + "messageSentHandler": [63, [], + [EmberOutgoingMessageType, uint16_t, EmberApsFrame, uint8_t, EmberStatus, LVBytes] + ], + "sendManyToOneRouteRequest": [65, [uint16_t, uint8_t], + [EmberStatus] + ], + "pollForData": [66, [uint16_t, EmberEventUnits, uint8_t], + [EmberStatus] + ], + "pollCompleteHandler": [67, [], + [EmberStatus] + ], + "pollHandler": [68, [], + [EmberNodeId] + ], + "incomingSenderEui64Handler": [98, [], + [EmberEUI64] + ], + "incomingMessageHandler": [69, [], + [EmberIncomingMessageType, EmberApsFrame, uint8_t, int8s, EmberNodeId, uint8_t, uint8_t, LVBytes] + ], + "incomingRouteRecordHandler": [89, [], + [EmberNodeId, EmberEUI64, uint8_t, int8s, LVBytes] + ], + "incomingManyToOneRouteRequestHandler": [125, [], + [EmberNodeId, EmberEUI64, uint8_t] + ], + "incomingRouteErrorHandler": [128, [], + [EmberStatus, EmberNodeId] + ], + "addressTableEntryIsActive": [91, [uint8_t], + [Bool] + ], + "setAddressTableRemoteEui64": [92, [uint8_t, EmberEUI64], + [EmberStatus] + ], + "setAddressTableRemoteNodeId": [93, [uint8_t, EmberNodeId], + [] + ], + "getAddressTableRemoteEui64": [94, [uint8_t], + [EmberEUI64] + ], + "getAddressTableRemoteNodeId": [95, [uint8_t], + [EmberNodeId] + ], + "setExtendedTimeout": [126, [EmberEUI64, Bool], + [] + ], + "getExtendedTimeout": [127, [EmberEUI64], + [Bool] + ], + "replaceAddressTableEntry": [130, [uint8_t, EmberEUI64, EmberNodeId, Bool], + [EmberStatus, EmberEUI64, EmberNodeId, Bool] + ], + "lookupNodeIdByEui64": [96, [EmberEUI64], + [EmberNodeId] + ], + "lookupEui64ByNodeId": [97, [EmberNodeId], + [EmberStatus, EmberEUI64] + ], + "getMulticastTableEntry": [99, [uint8_t], + [EmberStatus, EmberMulticastTableEntry] + ], + "setMulticastTableEntry": [100, [uint8_t, EmberMulticastTableEntry], + [EmberStatus] + ], + "idConflictHandler": [124, [], + [EmberNodeId] + ], + "sendRawMessage": [150, [LVBytes], + [EmberStatus] + ], + "macPassthroughMessageHandler": [151, [], + [EmberMacPassthroughType, uint8_t, int8s, LVBytes] + ], + "macFilterMatchMessageHandler": [70, [], + [uint8_t, EmberMacPassthroughType, uint8_t, int8s, LVBytes] + ], + "rawTransmitCompleteHandler": [152, [], + [EmberStatus] + ], + "setInitialSecurityState": [104, [EmberInitialSecurityState], + [EmberStatus] + ], + "getCurrentSecurityState": [105, [], + [EmberStatus, EmberCurrentSecurityState] + ], + "getKey": [106, [EmberKeyType], + [EmberStatus, EmberKeyStruct] + ], + "switchNetworkKeyHandler": [110, [], + [uint8_t] + ], + "getKeyTableEntry": [113, [uint8_t], + [EmberStatus, EmberKeyStruct] + ], + "setKeyTableEntry": [114, [uint8_t, EmberEUI64, Bool, EmberKeyData], + [EmberStatus] + ], + "findKeyTableEntry": [117, [EmberEUI64, Bool], + [uint8_t] + ], + "addOrUpdateKeyTableEntry": [102, [EmberEUI64, Bool, EmberKeyData], + [EmberStatus] + ], + "eraseKeyTableEntry": [118, [uint8_t], + [EmberStatus] + ], + "clearKeyTable": [177, [], + [EmberStatus] + ], + "requestLinkKey": [20, [EmberEUI64], + [EmberStatus] + ], + "zigbeeKeyEstablishmentHandler": [155, [], + [EmberEUI64, EmberKeyStatus] + ], + "addTransientLinkKey": [175, [EmberEUI64, EmberKeyData], + [EmberStatus] + ], + "clearTransientLinkKeys": [107, Array(), + [] + ], + "setSecurityKey": [202, [EmberKeyData, SecureEzspSecurityType], + [EzspStatus] + ], + "setSecurityParameters": [203, [SecureEzspSecurityLevel, SecureEzspRandomNumber], + [EzspStatus, SecureEzspRandomNumber] + ], + "resetToFactoryDefaults": [204, [], + [EzspStatus] + ], + "getSecurityKeyStatus": [205, [], + [EzspStatus, SecureEzspSecurityType] + ], + "trustCenterJoinHandler": [36, [], + [EmberNodeId, EmberEUI64, EmberDeviceUpdate, EmberJoinDecision, EmberNodeId] + ], + "broadcastNextNetworkKey": [115, [EmberKeyData], + [EmberStatus] + ], + "broadcastNetworkKeySwitch": [116, [], + [EmberStatus] + ], + "becomeTrustCenter": [119, [EmberKeyData], + [EmberStatus] + ], + "aesMmoHash": [111, [EmberAesMmoHashContext, Bool, LVBytes], + [EmberStatus, EmberAesMmoHashContext] + ], + "removeDevice": [168, [EmberNodeId, EmberEUI64, EmberEUI64], + [EmberStatus] + ], + "unicastNwkKeyUpdate": [169, [EmberNodeId, EmberEUI64, EmberKeyData], + [EmberStatus] + ], + "generateCbkeKeys": [164, [], + [EmberStatus] + ], + "generateCbkeKeysHandler": [158, [], + [EmberStatus, EmberPublicKeyData] + ], + "calculateSmacs": [159, [Bool, EmberCertificateData, EmberPublicKeyData], + [EmberStatus] + ], + "calculateSmacsHandler": [160, [], + [EmberStatus, EmberSmacData, EmberSmacData] + ], + "generateCbkeKeys283k1": [232, [], + [EmberStatus] + ], + "generateCbkeKeysHandler283k1": [233, [], + [EmberStatus, EmberPublicKey283k1Data] + ], + "calculateSmacs283k1": [234, [Bool, EmberCertificate283k1Data, EmberPublicKey283k1Data], + [EmberStatus] + ], + "calculateSmacsHandler283k1": [235, [], + [EmberStatus, EmberSmacData, EmberSmacData] + ], + "clearTemporaryDataMaybeStoreLinkKey": [161, [Bool], + [EmberStatus] + ], + "clearTemporaryDataMaybeStoreLinkKey283k1": [238, [Bool], + [EmberStatus] + ], + "getCertificate": [165, [], + [EmberStatus, EmberCertificateData] + ], + "getCertificate283k1": [236, [], + [EmberStatus, EmberCertificate283k1Data] + ], + "dsaSign": [166, [LVBytes], + [EmberStatus] + ], + "dsaSignHandler": [167, [], + [EmberStatus, LVBytes] + ], + "dsaVerify": [163, [EmberMessageDigest, EmberCertificateData, EmberSignatureData], + [EmberStatus] + ], + "dsaVerifyHandler": [120, [], + [EmberStatus] + ], + "dsaVerify283k1": [176, [EmberMessageDigest, EmberCertificate283k1Data, EmberSignature283k1Data], + [EmberStatus] + ], + "setPreinstalledCbkeData": [162, [EmberPublicKeyData, EmberCertificateData, EmberPrivateKeyData], + [EmberStatus] + ], + "setPreinstalledCbkeData283k1": [237, [EmberPublicKey283k1Data, EmberCertificate283k1Data, EmberPrivateKey283k1Data], + [EmberStatus] + ], + "mfglibStart": [131, [Bool], + [EmberStatus] + ], + "mfglibEnd": [132, [], + [EmberStatus] + ], + "mfglibStartTone": [133, [], + [EmberStatus] + ], + "mfglibStopTone": [134, [], + [EmberStatus] + ], + "mfglibStartStream": [135, [], + [EmberStatus] + ], + "mfglibStopStream": [136, [], + [EmberStatus] + ], + "mfglibSendPacket": [137, [LVBytes], + [EmberStatus] + ], + "mfglibSetChannel": [138, [uint8_t], + [EmberStatus] + ], + "mfglibGetChannel": [139, [], + [uint8_t] + ], + "mfglibSetPower": [140, [uint16_t, int8s], + [EmberStatus] + ], + "mfglibGetPower": [141, [], + [int8s] + ], + "mfglibRxHandler": [142, [], + [uint8_t, int8s, LVBytes] + ], + "launchStandaloneBootloader": [143, [uint8_t], + [EmberStatus] + ], + "sendBootloadMessage": [144, [Bool, EmberEUI64, LVBytes], + [EmberStatus] + ], + "getStandaloneBootloaderVersionPlatMicroPhy": [145, [], + [uint16_t, uint8_t, uint8_t, uint8_t] + ], + "incomingBootloadMessageHandler": [146, [], + [EmberEUI64, uint8_t, int8s, LVBytes] + ], + "bootloadTransmitCompleteHandler": [147, [], + [EmberStatus, LVBytes] + ], + "aesEncrypt": [148, [fixed_list(16, uint8_t), fixed_list(16, uint8_t)], + [fixed_list(16, uint8_t)] + ], + "overrideCurrentChannel": [149, [uint8_t], + [EmberStatus] + ], + "zllNetworkOps": [178, [EmberZllNetwork, EzspZllNetworkOperation, int8s], + [EmberStatus] + ], + "zllSetInitialSecurityState": [179, [EmberKeyData, EmberZllInitialSecurityState], + [EmberStatus] + ], + "zllStartScan": [180, [uint32_t, int8s, EmberNodeType], + [EmberStatus] + ], + "zllSetRxOnWhenIdle": [181, [uint16_t], + [EmberStatus] + ], + "zllNetworkFoundHandler": [182, [], + [EmberZllNetwork, Bool, EmberZllDeviceInfoRecord, uint8_t, int8s] + ], + "zllScanCompleteHandler": [183, [], + [EmberStatus] + ], + "zllAddressAssignmentHandler": [184, [], + [EmberZllAddressAssignment, uint8_t, int8s] + ], + "setLogicalAndRadioChannel": [185, [uint8_t], + [EmberStatus] + ], + "getLogicalChannel": [186, [], + [uint8_t] + ], + "zllTouchLinkTargetHandler": [187, [], + [EmberZllNetwork] + ], + "zllGetTokens": [188, [], + [EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity] + ], + "zllSetDataToken": [189, [EmberTokTypeStackZllData], + [] + ], + "zllSetNonZllNetwork": [191, Array(), + [] + ], + "isZllNetwork": [190, [], + [Bool] + ], + "rf4ceSetPairingTableEntry": [208, [uint8_t, EmberRf4cePairingTableEntry], + [EmberStatus] + ], + "rf4ceGetPairingTableEntry": [209, [uint8_t], + [EmberStatus, EmberRf4cePairingTableEntry] + ], + "rf4ceDeletePairingTableEntry": [210, [uint8_t], + [EmberStatus] + ], + "rf4ceKeyUpdate": [211, [uint8_t, EmberKeyData], + [EmberStatus] + ], + "rf4ceSend": [212, [uint8_t, uint8_t, uint16_t, EmberRf4ceTxOption, uint8_t, LVBytes], + [EmberStatus] + ], + "rf4ceIncomingMessageHandler": [213, [], + [uint8_t, uint8_t, uint16_t, EmberRf4ceTxOption, LVBytes] + ], + "rf4ceMessageSentHandler": [214, [], + [EmberStatus, uint8_t, EmberRf4ceTxOption, uint8_t, uint16_t, uint8_t, LVBytes] + ], + "rf4ceStart": [215, [EmberRf4ceNodeCapabilities, EmberRf4ceVendorInfo, int8s], + [EmberStatus] + ], + "rf4ceStop": [216, [], + [EmberStatus] + ], + "rf4ceDiscovery": [217, [EmberPanId, EmberNodeId, uint8_t, uint16_t, LVBytes], + [EmberStatus] + ], + "rf4ceDiscoveryCompleteHandler": [218, [], + [EmberStatus] + ], + "rf4ceDiscoveryRequestHandler": [219, [], + [EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t, uint8_t] + ], + "rf4ceDiscoveryResponseHandler": [220, [], + [Bool, uint8_t, EmberPanId, EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t, uint8_t] + ], + "rf4ceEnableAutoDiscoveryResponse": [221, [uint16_t], + [EmberStatus] + ], + "rf4ceAutoDiscoveryResponseCompleteHandler": [222, [], + [EmberStatus, EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t] + ], + "rf4cePair": [223, [uint8_t, EmberPanId, EmberEUI64, uint8_t], + [EmberStatus] + ], + "rf4cePairCompleteHandler": [224, [], + [EmberStatus, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo] + ], + "rf4cePairRequestHandler": [225, [], + [EmberStatus, uint8_t, EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t] + ], + "rf4ceUnpair": [226, [uint8_t], + [EmberStatus] + ], + "rf4ceUnpairHandler": [227, [], + [uint8_t] + ], + "rf4ceUnpairCompleteHandler": [228, [], + [uint8_t] + ], + "rf4ceSetPowerSavingParameters": [229, [uint32_t, uint32_t], + [EmberStatus] + ], + "rf4ceSetFrequencyAgilityParameters": [230, [uint8_t, uint8_t, int8s, uint16_t, uint8_t], + [EmberStatus] + ], + "rf4ceSetApplicationInfo": [231, [EmberRf4ceApplicationInfo], + [EmberStatus] + ], + "rf4ceGetApplicationInfo": [239, [], + [EmberStatus, EmberRf4ceApplicationInfo] + ], + "rf4ceGetMaxPayload": [243, [uint8_t, EmberRf4ceTxOption], + [uint8_t] + ], + "rf4ceGetNetworkParameters": [244, [], + [EmberStatus, EmberNodeType, EmberNetworkParameters] + ], + "gpProxyTableProcessGpPairing": [201, [uint32_t, EmberGpAddress, uint8_t, uint16_t, uint16_t, uint16_t, fixed_list(8, uint8_t), EmberKeyData], + [] + ], + "dGpSend": [198, [Bool, Bool, EmberGpAddress, uint8_t, LVBytes, uint8_t, uint16_t], + [EmberStatus] + ], + "dGpSentHandler": [199, [], + [EmberStatus, uint8_t] + ], + "gpepIncomingMessageHandler": [197, [], + [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] + ] +}; + +//# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts new file mode 100644 index 0000000000..27e5f7a312 --- /dev/null +++ b/src/adapter/ezsp/driver/driver.ts @@ -0,0 +1,249 @@ +import { Ezsp } from './ezsp'; +import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; +import { EventEmitter } from "events"; +import { EmberApsFrame, EmberNetworkParameters } from './types/struct'; +import { Deferred } from './utils'; +import { EmberOutgoingMessageType, EmberEUI64, EmberDeviceUpdate } from './types/named'; + + +export class Driver extends EventEmitter { + private direct = EmberOutgoingMessageType.OUTGOING_DIRECT + private ezsp: Ezsp; + private eui64ToNodeId = new Map(); + private pending = new Map>>(); + + constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ + super(); + + if (!nodeInfo) return; + + for(let node of nodeInfo){ + let eui64 = node.eui64 instanceof EmberEUI64 ? node.eui64 : new EmberEUI64(node.eui64); + this.eui64ToNodeId.set(eui64.toString(), node.nodeId); + } + } + + public async startup(port: string, options: {}, logger: any) { + let ezsp = this.ezsp = new Ezsp(logger); + await ezsp.connect(port, options); + const version = await ezsp.version(); + console.log('Got version', version); + + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SECURITY_LEVEL, 5); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS, + EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS + | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + + if (await ezsp.networkInit()) { + console.log('Network ready'); + ezsp.on('frame', this.handleFrame.bind(this)) + } else { + const state = await ezsp.execCommand('networkState'); + console.log('Network state', state); + } + } + + private handleFrame(frameName: string, ...args: any[]) { + + if (frameName === 'incomingMessageHandler') { + let [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; + let eui64; + for(let [k,v] of this.eui64ToNodeId){ + if (v === sender){ + eui64 = k; + break; + } + } + super.emit('incomingMessage', { + messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, + senderEui64: eui64 + }); + + let isReply = false; + let tsn = -1; + let commandId = 0; + if (isReply) { + this.handleReply(sender, apsFrame, tsn, commandId, args); + } + } else if (frameName === 'messageSentHandler') { + if (args[4] != 0) { + this.handleFrameFailure.apply(this, args); + } else { + this.handleFrameSent.apply(this, args); + } + } else if (frameName === 'trustCenterJoinHandler') { + if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { + this.handleNodeLeft.apply(this, args); + } else { + this.handleNodeJoined.apply(this, args); + } + } + + } + + private handleNodeLeft(nwk: number, ieee: EmberEUI64 | number[], ...args: any[]) { + if (ieee && !(ieee instanceof EmberEUI64)) { + ieee = new EmberEUI64(ieee); + } + this.eui64ToNodeId.delete(ieee.toString()); + this.emit('deviceLeft', [nwk, ieee]) + } + + private handleNodeJoined(nwk: number, ieee: EmberEUI64 | number[], deviceUpdate: any, joinDec: any, parentNwk: any) { + if (ieee && !(ieee instanceof EmberEUI64)) { + ieee = new EmberEUI64(ieee); + } + this.eui64ToNodeId.set(ieee.toString(), nwk); + this.emit('deviceJoined', [nwk, ieee]) + } + + private handleReply(sender: number, apsFrame: EmberApsFrame, tsn: number, commandId: number, ...args: any[]) { + try { + var arr = this.pending.get(tsn); + if (!arr) { + console.log('Unexpected reponse TSN=', tsn, 'command=', commandId, args) + return; + }; + let [sendDeferred, replyDeferred] = arr; + if (sendDeferred.isFullfilled) { + this.pending.delete(tsn); + } + replyDeferred.resolve(args); + return; + } catch (e) { + console.log(e); + return; + } + } + + public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { + + let seq = apsFrame.sequence; + console.assert(!this.pending.has(seq)); + let sendDeferred = new Deferred(); + let replyDeferred = new Deferred(); + this.pending.set(seq, [sendDeferred, replyDeferred]); + + let handle; + try { + + if (timeout > 0) { + handle = setTimeout(() => { + throw new Error('Timeout while waiting for reply'); + }, timeout); + } + + + if (typeof nwk !== 'number') { + let eui64 = nwk as EmberEUI64; + let strEui64 = eui64.toString(); + let nodeId = this.eui64ToNodeId.get(strEui64); + if (nodeId === undefined) { + nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64).then(arr => arr[0]); + if (nodeId && nodeId !== 0xFFFF) { + this.eui64ToNodeId.set(strEui64, nodeId); + } else { + throw new Error('Unknown EUI64:' + strEui64); + } + } + nwk = nodeId; + } + + let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + console.log('unicast message sent, waiting for reply'); + if (v[0] != 0) { + this.pending.delete(seq); + sendDeferred.reject(false); + replyDeferred.reject(false); + throw new Error(`Message send failure ${v[0]}`) + } + + await sendDeferred.promise; + if (timeout > 0) { + await replyDeferred.promise; + } else { + this.pending.delete(seq); + } + return true; + } catch (e) { + return false; + } finally { + if (handle) + clearTimeout(handle); + } + } + + private handleFrameFailure(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { + try { + var arr = this.pending.get(messageTag); + if (!arr) { + console.log("Unexpected message send failure"); + return; + } + this.pending.delete(messageTag); + let [sendDeferred,] = arr; + let e = new Error('Message send failure:' + status); + console.log(e); + sendDeferred.reject(e); + //replyDeferred.reject(e); + } catch (e) { + console.log(e); + } + } + + private handleFrameSent(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { + try { + var arr = this.pending.get(messageTag); + if (!arr) { + console.log("Unexpected message send notification"); + return; + } + let [sendDeferred, replyDeferred] = arr; + if (replyDeferred.isFullfilled) { + this.pending.delete(messageTag); + } + sendDeferred.resolve(true); + } catch (e) { + console.log(e); + } + } + + public stop() { + return this.ezsp.close(); + } + + public getLocalEUI64(): Promise { + return this.ezsp.execCommand('getEui64') + .then(ret => new EmberEUI64(ret[0] as any)); + } + + public async networkIdToEUI64(nwk: number): Promise { + for (let [eUI64, value] of this.eui64ToNodeId) { + if (value === nwk) return new EmberEUI64(eUI64); + } + let value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); + if (value[0] === 0) { + let eUI64 = new EmberEUI64(value[1] as any); + this.eui64ToNodeId.set(eUI64.toString(), nwk); + return eUI64; + } else { + throw new Error('Unrecognized nodeId:' + nwk) + } + } + + public permitJoining(seconds:number){ + return this.ezsp.execCommand('permitJoining', seconds); + } + + public async getNetworkParameters() : Promise<{nodeType: number, networkParams: EmberNetworkParameters}> { + let [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); + // kirov 0x93 NOT_JOINED + if (status != 0 && status != 0x93) + throw new Error('Unable to obtain network parameters'); + return {nodeType, networkParams} + } +} \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts new file mode 100644 index 0000000000..0b5bec5235 --- /dev/null +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -0,0 +1,214 @@ +import * as t from './types'; +import { UartProtocol } from './uart'; +import { COMMANDS } from './commands'; + +import { Deferred } from './utils'; +import { EmberStatus, EmberOutgoingMessageType } from './types/named'; +import { EventEmitter } from 'events'; +import { EmberApsFrame } from './types/struct'; + +export class Ezsp extends EventEmitter { + ezsp_version = 4; + _gw: UartProtocol; + _port : any; + _seq = 0; + _awaiting = new Map }>(); + COMMANDS_BY_ID = new Map(); + _cbCounter = 0; + logger: any; + + constructor(logger: any) { + super(); + this.logger = logger; + for (let name in COMMANDS) { + let details = (COMMANDS)[name]; + this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); + } + } + + async connect(device: string, options: {}) { + console.assert(!this._gw); + [this._gw, this._port] = await UartProtocol.connect(device, options, this.logger); + this.startReadQueue(); + } + + private async startReadQueue() { + for await (let frame of this._gw) { + try { + this.frame_received(frame); + } catch (e) { + this.logger('Error handling frame', e); + } + } + } + + reset() { + return this._gw.reset(); + } + + async version() { + let version = this.ezsp_version; + let result = await this._command("version", version); + if ((result[0] !== version)) { + this.logger("Switching to eszp version %d", result[0]); + await this._command("version", result[0]); + } + return result[0]; + } + + async networkInit() { + let result; + [result] = await this._command("networkInit"); + console.log('network init result', result); + return result === EmberStatus.SUCCESS; + } + + async setConfigurationValue(configId: number, value: any) { + let ret; + [ret] = await this.execCommand('setConfigurationValue', configId, value); + console.assert(ret === 0); + this.logger('Set %s = %s', configId, value); + } + + close() { + return this._port.close(); + } + + private _ezsp_frame(name: string, ...args: any[]) { + var c, data, frame, cmd_id; + c = (COMMANDS)[name]; + data = t.serialize(args, c[1]); + frame = [(this._seq & 255)]; + // kirov + if ((this.ezsp_version < 8)) { + if ((this.ezsp_version >= 5)) { + frame.push(0x00, 0xFF, 0x00, c[0]); + } else { + frame.push(0x00, c[0]); + } + } else { + cmd_id = t.serialize([c[0]], [t.uint16_t]); + frame.push(0x00, 0x01, ...cmd_id); + } + return Buffer.concat([Buffer.from(frame), data]); + } + + private _command(name: string, ...args: any[]): Promise { + var c, data, deferred; + this.logger("Send command %s", name); + data = this._ezsp_frame(name, ...args); + this._gw.data(data); + c = (COMMANDS)[name]; + deferred = new Deferred(); + this._awaiting.set(this._seq, { expectedId: c[0], schema: c[2], deferred }); + this._seq = (this._seq + 1 % 256); + return deferred.promise; + } + + /* private async _list_command(name: string, item_frames: Array, completion_frame: any, spos: number, ...args: any[]) { + // Run a command, returning result callbacks as a list + var cbid, fut: Deferred, v; + var cb; + fut = new Deferred(); + let results: any[] = []; + cb = (frameName: string, response: any) => { + if (item_frames.indexOf(frameName) >= 0) { + results.push(response); + } else { + if ((frameName === completion_frame)) { + fut.resolve(response); + } + } + }; + cbid = this.add_callback(cb); + try { + v = await this._command(name, ...args); + if ((v[0] !== 0)) { + throw new Error(v); + } + v = await fut.promise; + if ((v[spos] !== 0)) { + throw new Error(v); + } + } finally { + this.remove_callback(cbid); + } + return results; + }*/ + + + async formNetwork(parameters: {}) { + var fut: Deferred, v; + fut = new Deferred(); + this.on('frame', (frameName: string, response: any) => { + if ((frameName === "stackStatusHandler")) { + fut.resolve(response); + } + }) + v = await this._command("formNetwork", parameters); + if ((v[0] !== 0)) { + throw new Error(("Failure forming network:" + v)); + } + v = await fut.promise; + if ((v[0] !== 0x90 /*EmberStatus.NETWORK_UP*/)) { + throw new Error(("Failure forming network:" + v)); + } + return v; + } + + execCommand(name: string, ...args: any[]) { + if (Object.keys(COMMANDS).indexOf(name) < 0) { + throw new Error('Unknown command: ' + name); + } + return this._command(name, ...args); + } + + frame_received(data: Buffer) { + /*Handle a received EZSP frame + + The protocol has taken care of UART specific framing etc, so we should + just have EZSP application stuff here, with all escaping/stuffing and + data randomization removed. + */ + var frame_id: number, result, schema, sequence; + // kirov + if ((this.ezsp_version < 8)) { + [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; + } else { + sequence = data[0]; + [[frame_id], data] = t.deserialize(data.slice(3), [t.uint16_t]); + } + if ((frame_id === 255)) { + frame_id = 0; + if ((data.length > 1)) { + frame_id = data[1]; + data = data.slice(2); + } + } + let cmd = this.COMMANDS_BY_ID.get(frame_id); + if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); + let frameName = cmd.name; + this.logger("Application frame %s (%s) received", frame_id, frameName); + if (this._awaiting.has(sequence)) { + let entry = this._awaiting.get(sequence); + this._awaiting.delete(sequence); + if (entry) { + console.assert(entry.expectedId === frame_id); + [result, data] = t.deserialize(data, entry.schema); + entry.deferred.resolve(result); + } + } else { + schema = cmd.outArgs; + frameName = cmd.name; + [result, data] = t.deserialize(data, schema); + super.emit('frame', frameName, ...result); + } + if ((frame_id === 0)) { + this.ezsp_version = result[0]; + } + } + + public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { + return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); + } +} diff --git a/src/adapter/ezsp/driver/index.ts b/src/adapter/ezsp/driver/index.ts new file mode 100644 index 0000000000..bbf9987ce9 --- /dev/null +++ b/src/adapter/ezsp/driver/index.ts @@ -0,0 +1,3 @@ +import { Ezsp } from './ezsp'; +import { Driver } from './driver'; +export { Ezsp, Driver } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/test.ts b/src/adapter/ezsp/driver/test.ts new file mode 100644 index 0000000000..38297dcb6e --- /dev/null +++ b/src/adapter/ezsp/driver/test.ts @@ -0,0 +1,37 @@ +// import { ControllerApplication } from './application'; +// import { EmberApsFrame } from './types/struct'; +// import { EmberApsOption, EmberEUI64 } from './types/named'; + +// const application = new ControllerApplication(); + +// application.on('incomingMessage', ({ apsFrame, sender, senderEui64, message }: { apsFrame: EmberApsFrame, sender: number, senderEui64: EmberEUI64, message: Buffer }) => { +// console.log('incomingMessage', sender, senderEui64 && senderEui64.toString(), apsFrame, message); +// }); + +// application.startup('/dev/ttyUSB1', { +// baudRate: 57600, +// parity: 'none', +// stopBits: 1, +// xon: true, +// xoff: true +// }).then(async () => { + +// let localEui64 = await application.getLocalEUI64(); +// console.log('Local Eui64:', localEui64.toString()); + +// var res = await application.request(0xA329, { +// clusterId: 0x11, profileId: 0xC105, +// sequence: 1, +// sourceEndpoint: 0xE8, destinationEndpoint: 0xE8, +// options: EmberApsOption.APS_OPTION_FORCE_ROUTE_DISCOVERY | EmberApsOption.APS_OPTION_RETRY +// }, Buffer.from('\nTESTING!\n'), 0); + +// console.log('Sent=', res); + +// console.log('nodeID:', (await application.networkIdToEUI64(41769)).toString()); + +// var nwkParams = await application.getNetworkParameters(); +// console.log(nwkParams); +// }); + + diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts new file mode 100644 index 0000000000..ccf721e065 --- /dev/null +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -0,0 +1,167 @@ +export class int_t { + static _signed = true + + static serialize(cls: any, value: number) { + let buffer = Buffer.alloc(cls._size, 0); + if (cls._signed) { + buffer.writeIntLE(value, 0, cls._size); + } else { + buffer.writeUIntLE(value, 0, cls._size); + } + return buffer; + } + + static deserialize(cls: any, data: Buffer) { + return [cls._signed ? data.readIntLE(0, cls._size) : data.readUIntLE(0, cls._size), data.slice(cls._size)] + } +} + +export class int8s extends int_t { + static _size = 1 +} + +export class int16s extends int_t { + static _size = 2 +} + +export class int24s extends int_t { + static _size = 3 +} + +export class int32s extends int_t { + static _size = 4 +} + +export class int64s extends int_t { + static _size = 8 +} + +export class uint_t extends int_t { + static _signed = false +} + +export class uint8_t extends uint_t { + static _size = 1 +} + +export class uint16_t extends uint_t { + static _size = 2 +} + +export class uint24_t extends uint_t { + static _size = 3 +} + +export class uint32_t extends uint_t { + static _size = 4 +} + +export class uint64_t extends uint_t { + static _size = 8 +} + +/* +export class Single extends number { + serialize() { + return struct.pack(" i.serialize(cls, i)); + } + static deserialize(cls: any, data: Buffer): (any[] | Buffer)[] { + var item; + var r: any[] = []; + while (data) { + [item, data] = (r)._itemtype.deserialize((r)._itemtype, data); + r.push(item); + } + return [r, data]; + } +} + +class _LVList extends List { + static serialize(cls: any, value: any[]) { + var data, head; + head = [cls.length]; + data = super.serialize(cls, value); + return head.concat(data); + } + static deserialize(cls: any, data: Buffer) { + var item, length; + var r: any[] = []; + [length, data] = [data[0], data.slice(1)]; + for (var i = 0; i < length; i++) { + [item, data] = (r)._itemtype.deserialize((r)._itemtype, data); + r.push(item); + } + return [r, data]; + } +} +export function list(itemtype: any) : List { + class ConreteList extends List { + } + (LVList.prototype)['_itemtype'] = itemtype; + return ConreteList; +} + +export function LVList(itemtype: any) : List { + class LVList extends _LVList { + } + (LVList.prototype)['itemtype'] = itemtype; + return LVList; +} + +class _FixedList extends List { + static deserialize(cls: any, data: Buffer) { + let item; + let r: any[] = []; + for (var i = 0; i < cls._length; i++) { + [item, data] = cls._itemtype.deserialize(cls._itemtype, data); + r.push(item); + } + return [r, data]; + } +} + +export function fixed_list(length: number, itemtype: any) : { + new(): any; + deserialize(cls : any, data : Buffer) : any; +} { + class FixedList extends _FixedList { + static _itemtype = itemtype; + static _length = length; + } + return FixedList; +} \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts new file mode 100644 index 0000000000..5fa37b371a --- /dev/null +++ b/src/adapter/ezsp/driver/types/index.ts @@ -0,0 +1,82 @@ +import { + int8s, + uint_t, + uint8_t, + uint16_t, + uint24_t, + uint32_t, + uint64_t, + LVBytes, + list, + LVList, + fixed_list +} from './basic'; + +import { + NcpResetCode, EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberRf4ceApplicationCapabilities, EmberNodeId, + EmberPanId, EmberMulticastId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, + EmberGpKeyType, SecureEzspRandomNumber, SecureEzspSessionId, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, EzspEndpointFlags, + EmberConfigTxPowerMode, EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, + EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, + EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, + EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, + EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask +} from './named'; + +import { + EzspStruct, EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, + EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, + EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, + EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, + EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry +} from './struct' + +export function deserialize(payload: any, schema: any[]) { + let result = []; + let value, data = payload; + for (let type of schema) { + [value, data] = type.deserialize(type, data) + result.push(value) + } + return [result, data]; +} + +export function serialize(data: any[], schema: { serialize: Function }[]) { + return Buffer.concat(schema.map((s, idx) => s.serialize(s, data[idx]))); +} + +export { + /* Basic Types */ + int8s, + uint_t, + uint8_t, + uint16_t, + uint24_t, + uint32_t, + uint64_t, + LVBytes, + list, + LVList, + fixed_list, + + /* Named Types */ + NcpResetCode, EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberRf4ceApplicationCapabilities, EmberNodeId, + EmberPanId, EmberMulticastId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, + EmberGpKeyType, SecureEzspRandomNumber, SecureEzspSessionId, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, EzspEndpointFlags, + EmberConfigTxPowerMode, EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, + EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, + EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, + EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, + EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, + + /* Structs */ + EzspStruct, EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, + EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, + EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, + EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, + EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry +} \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts new file mode 100644 index 0000000000..680cbca68c --- /dev/null +++ b/src/adapter/ezsp/driver/types/named.ts @@ -0,0 +1,1647 @@ +import * as basic from './basic'; +import { fixed_list } from './basic'; + +console.assert(basic.uint8_t); + +export class NcpResetCode extends basic.uint8_t { + //Reset and Error Codes for NCP + static RESET_UNKNOWN_REASON = 0x00 + static RESET_EXTERNAL = 0x01 + static RESET_POWER_ON = 0x02 + static RESET_WATCHDOG = 0x03 + static RESET_ASSERT = 0x06 + static RESET_BOOTLOADER = 0x09 + static RESET_SOFTWARE = 0x0B + static ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51 + static ERROR_UNKNOWN_EM3XX_ERROR = 0x80 +} + +export class EmberRf4ceTxOption extends basic.uint8_t { +} +export class EmberRf4ceNodeCapabilities extends basic.uint8_t { +} +export class EmberRf4ceApplicationCapabilities extends basic.uint8_t { +} +export class EmberNodeId extends basic.uint16_t { +} +export class EmberPanId extends basic.uint16_t { +} +export class EmberMulticastId extends basic.uint16_t { +} + +export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { + + private _str: string; + + constructor(private _value: ArrayLike | string) { + super() + if (typeof (_value) === 'string') { + if ((_value as string).length !== 16) { + throw new Error('Incorrect value passed'); + } + this._str = _value; + this._value = Buffer.from(_value, 'hex'); + } else { + if (_value.length !== 8) { + throw new Error('Incorrect value passed'); + } + this._str = Buffer.from(_value as any).toString('hex'); + } + } + + static deserialize(cls: any, data: Buffer) { + var r; + var arr = super.deserialize(cls, data); + r = arr[0]; + data = arr[1] as Buffer; + return [(r as number[]).reverse(), data]; + } + static serialize(cls: any, value: number[] | EmberEUI64) { + if (value instanceof EmberEUI64){ + value = (value as EmberEUI64).value as number[]; + } + console.assert(cls._length === value.length); + return (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)); + } + + public get value() { + return this._value; + } + + public toString() { + return this._str + } + + /* + + __hash__() { + return hash(repr(this)); + }*/ +} +export class EmberLibraryStatus extends basic.uint8_t { +} +export class SecureEzspSecurityType extends basic.uint32_t { +} +export class SecureEzspSecurityLevel extends basic.uint8_t { +} +export class EmberGpSecurityLevel extends basic.uint8_t { +} +export class EmberGpKeyType extends basic.uint8_t { +} +export class SecureEzspRandomNumber extends basic.uint64_t { +} +export class SecureEzspSessionId extends basic.uint64_t { +} +export class Bool extends basic.uint8_t { + static false = 0x00 // An alias for zero, used for clarity. + static true = 0x01 // An alias for one, used for clarity. +} + +export class EzspConfigId extends basic.uint8_t { + // Identifies a configuration value. + + // The number of packet buffers available to the stack. When set to the + // special value 0xFF, the NCP will allocate all remaining configuration RAM + // towards packet buffers, such that the resulting count will be the largest + // whole number of packet buffers that can fit into the available memory. + static CONFIG_PACKET_BUFFER_COUNT = 0x01 + // The maximum number of router neighbors the stack can keep track of. A + // neighbor is a node within radio range. + static CONFIG_NEIGHBOR_TABLE_SIZE = 0x02 + // The maximum number of APS retried messages the stack can be transmitting + // at any time. + static CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03 + // The maximum number of non-volatile bindings supported by the stack. + static CONFIG_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 ::CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE). + static CONFIG_ADDRESS_TABLE_SIZE = 0x05 + // The maximum number of multicast groups that the device may be a member + // of. + static CONFIG_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. + static CONFIG_ROUTE_TABLE_SIZE = 0x07 + // The number of simultaneous route discoveries that a node will support. + static CONFIG_DISCOVERY_TABLE_SIZE = 0x08 + // The size of the alarm broadcast buffer. + static CONFIG_BROADCAST_ALARM_DATA_SIZE = 0x09 + // The size of the unicast alarm buffers allocated for end device children. + static CONFIG_UNICAST_ALARM_DATA_SIZE = 0x0A + // Specifies the stack profile. + static CONFIG_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). + static CONFIG_SECURITY_LEVEL = 0x0D + // The maximum number of hops for a message. + static CONFIG_MAX_HOPS = 0x10 + // The maximum number of end device children that a router will support. + static CONFIG_MAX_END_DEVICE_CHILDREN = 0x11 + // The maximum amount of time that the MAC will hold a message for indirect + // transmission to a child. + static CONFIG_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. + static CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13 + // The maximum amount of time that a mobile node can wait between polls. If + // no poll is heard within this timeout, then the parent removes the mobile + // node from its tables. + static CONFIG_MOBILE_NODE_POLL_TIMEOUT = 0x14 + // The number of child table entries reserved for use only by mobile nodes. + static CONFIG_RESERVED_MOBILE_CHILD_ENTRIES = 0x15 + // Enables boost power mode and/or the alternate transmitter output. + static CONFIG_TX_POWER_MODE = 0x17 + // 0: Allow this node to relay messages. 1: Prevent this node from relaying + // messages. + static CONFIG_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 + // ::CONFIG_ADDRESS_TABLE_SIZE.) + static CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19 + // The size of the source route table. + static CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A + // The units used for timing out end devices on their parents. + static CONFIG_END_DEVICE_POLL_TIMEOUT_SHIFT = 0x1B + // The number of blocks of a fragmented message that can be sent in a single + // window. + static CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C + // The time the stack will wait (in milliseconds) between sending blocks of + // a fragmented message. + static CONFIG_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). + static CONFIG_KEY_TABLE_SIZE = 0x1E + // The APS ACK timeout value. The stack waits this amount of time between + // resends of APS retried messages. + static CONFIG_APS_ACK_TIMEOUT = 0x1F + // The duration of an active scan, in the units used by the 15.4 scan + // parameter (((1 << duration) + 1) * 15ms). This also controls the jitter + // used when responding to a beacon request. + static CONFIG_ACTIVE_SCAN_DURATION = 0x20 + // The time the coordinator will wait (in seconds) for a second end device + // bind request to arrive. + static CONFIG_END_DEVICE_BIND_TIMEOUT = 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. + static CONFIG_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. + static CONFIG_REQUEST_KEY_TIMEOUT = 0x24 + // 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. + static CONFIG_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 + // APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set. + static CONFIG_APPLICATION_ZDO_FLAGS = 0x2A + // The maximum number of broadcasts during a single broadcast timeout + // period. + static CONFIG_BROADCAST_TABLE_SIZE = 0x2B + // The size of the MAC filter list table. + static CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C + // The number of supported networks. + static CONFIG_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. + static CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E + // ZLL group address initial configuration. + static CONFIG_ZLL_GROUP_ADDRESSES = 0x2F + // ZLL rssi threshold initial configuration. + static CONFIG_ZLL_RSSI_THRESHOLD = 0x30 + // The maximum number of pairings supported by the stack. Controllers + // must support at least one pairing table entry while targets must + // support at least five. + static CONFIG_RF4CE_PAIRING_TABLE_SIZE = 0x31 + // The maximum number of outgoing RF4CE packets supported by the stack. + static CONFIG_RF4CE_PENDING_OUTGOING_PACKET_TABLE_SIZE = 0x32 + // Toggles the mtorr flow control in the stack. + static CONFIG_MTORR_FLOW_CONTROL = 0x33 + // Setting the retry queue size. + static CONFIG_RETRY_QUEUE_SIZE = 0x34 + // Setting the new broadcast entry threshold. + static CONFIG_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. + static CONFIG_TRANSIENT_KEY_TIMEOUT_S = 0x36 +} + + +export class EzspValueId extends basic.uint8_t { + // Identifies a value. + + // The contents of the node data stack token. + static VALUE_TOKEN_STACK_NODE_DATA = 0x00 + // The types of MAC passthrough messages that the host wishes to receive. + static VALUE_MAC_PASSTHROUGH_FLAGS = 0x01 + // The source address used to filter legacy EmberNet messages when the + // MAC_PASSTHROUGH_EMBERNET_SOURCE flag is set in + // VALUE_MAC_PASSTHROUGH_FLAGS. + static VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02 + // The number of available message buffers. + static VALUE_FREE_BUFFERS = 0x03 + // Selects sending synchronous callbacks in ezsp-uart. + static VALUE_UART_SYNCH_CALLBACKS = 0x04 + // The maximum incoming transfer size for the local node. + static VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05 + // The maximum outgoing transfer size for the local node. + static VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06 + // A boolean indicating whether stack tokens are written to persistent + // storage as they change. + static VALUE_STACK_TOKEN_WRITING = 0x07 + // A read-only value indicating whether the stack is currently performing a + // rejoin. + static VALUE_STACK_IS_PERFORMING_REJOIN = 0x08 + // A list of EmberMacFilterMatchData values. + static VALUE_MAC_FILTER_LIST = 0x09 + // The Ember Extended Security Bitmask. + static VALUE_EXTENDED_SECURITY_BITMASK = 0x0A + // The node short ID. + static VALUE_NODE_SHORT_ID = 0x0B + // The descriptor capability of the local node. + static VALUE_DESCRIPTOR_CAPABILITY = 0x0C + // The stack device request sequence number of the local node. + static VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D + // Enable or disable radio hold-off. + static VALUE_RADIO_HOLD_OFF = 0x0E + // The flags field associated with the endpoint data. + static VALUE_ENDPOINT_FLAGS = 0x0F + // Enable/disable the Mfg security config key settings. + static VALUE_MFG_SECURITY_CONFIG = 0x10 + // Retrieves the version information from the stack on the NCP. + static VALUE_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 + // REJOIN_REASON_NONE. The NCP will store the rejoin reason used by the + // call to emberFindAndRejoinNetwork() + static VALUE_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 REJOIN_DUE_TO_APP_EVENT_1. If the application + // wishes to denote its own rejoin reasons it can do so by calling + // ezspSetValue(VALUE_HOST_REJOIN_REASON, 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(VALUE_REAL_REJOIN_REASON). + static VALUE_LAST_REJOIN_REASON = 0x13 + // The next ZigBee sequence number. + static VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14 + // CCA energy detect threshold for radio. + static VALUE_CCA_THRESHOLD = 0x15 + // The threshold value for a counter + static VALUE_SET_COUNTER_THRESHOLD = 0x17 + // Resets all counters thresholds to 0xFF + static VALUE_RESET_COUNTER_THRESHOLDS = 0x18 + // Clears all the counters + static VALUE_CLEAR_COUNTERS = 0x19 + // The device RF4CE base channel + static VALUE_RF4CE_BASE_CHANNEL = 0x1A + // The RF4CE device types supported by the node + static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST = 0x1B + // The RF4CE profiles supported by the node + static VALUE_RF4CE_SUPPORTED_PROFILES_LIST = 0x1C + // Setting this byte enables R21 behavior on the NCP. + static VALUE_ENABLE_R21_BEHAVIOR = 0x29 + // Configure the antenna mode(0-primary,1-secondary,2- toggle on tx ack + // fail). + static VALUE_ANTENNA_MODE = 0x30 + // The GDP binding recipient parameters + static VALUE_RF4CE_GDP_BINDING_RECIPIENT_PARAMETERS = 0x1D + // The GDP binding push button stimulus received pending flag + static VALUE_RF4CE_GDP_PUSH_BUTTON_STIMULUS_RECEIVED_PENDING_FLAG = 0x1E + // The GDP originator proxy flag in the advanced binding options + static VALUE_RF4CE_GDP_BINDING_PROXY_FLAG = 0x1F + // The GDP application specific user s join unti_VALUE_RF4CE_MSO_USER_STRING + // 0x21 The MSO user string + static VALUE_RF4CE_GDP_APPLICATION_SPECIFIC_USER_STRING = 0x20 + // The MSO user string + static VALUE_RF4CE_MSO_USER_STRING = 0x21 + // The MSO binding recipient parameters + static VALUE_RF4CE_MSO_BINDING_RECIPIENT_PARAMETERS = 0x22 + // The NWK layer security frame counter value + static VALUE_NWK_FRAME_COUNTER = 0x23 + // The APS layer security frame counter value + static VALUE_APS_FRAME_COUNTER = 0x24 + // Sets the device type to use on the next rejoin using device type + static VALUE_RETRY_DEVICE_TYPE = 0x25 + // The device RF4CE base channel + static VALUE_RF4CE_BASE_CHANNEL2 = 0x26 + // The RF4CE device types supported by the node + static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST2 = 0x27 + // The RF4CE profiles supported by the node + static VALUE_RF4CE_SUPPORTED_PROFILES_LIST2 = 0x28 + // Enable or disable packet traffic arbitration. + static VALUE_ENABLE_PTA = 0x31 + // Set packet traffic arbitration configuration options. + static VALUE_PTA_OPTIONS = 0x32 + // Configure manufacturing library options(0-non-CSMA transmits,1-CSMA transmits). + static VALUE_MFGLIB_OPTIONS = 0x33 +} + +export class EzspExtendedValueId extends basic.uint8_t { + // 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. + + // The flags field associated with the specified endpoint. + static EXTENDED_VALUE_ENDPOINT_FLAGS = 0x00 + // 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 UNKNOWN_NODE_ID (0xFFFD). + static EXTENDED_VALUE_LAST_LEAVE_REASON = 0x01 + // This number of bytes of overhead required in the network frame for source + // routing to a particular destination. + static EXTENDED_VALUE_GET_SOURCE_ROUTE_OVERHEAD = 0x02 +} + + +export class EzspEndpointFlags extends basic.uint16_t { + // Flags associated with the endpoint data configured on the NCP. + + // Indicates that the endpoint is disabled and NOT discoverable via ZDO. + static ENDPOINT_DISABLED = 0x00 + // Indicates that the endpoint is enabled and discoverable via ZDO. + static ENDPOINT_ENABLED = 0x01 +} + + +export class EmberConfigTxPowerMode extends basic.uint16_t { + // Values for CONFIG_TX_POWER_MODE. + + // Normal power mode and bi-directional RF transmitter output. + static TX_POWER_MODE_DEFAULT = 0x00 + // Enable boost power mode. This is a high performance radio mode which + // offers increased receive sensitivity and transmit power at the cost of an + // increase in power consumption. + static TX_POWER_MODE_BOOST = 0x01 + // Enable the alternate transmitter output. This allows for simplified + // connection to an external power amplifier via the RF_TX_ALT_P and + // RF_TX_ALT_N pins. TX_POWER_MODE_BOOST_AND_ALTERNATE 0x03 Enable both + // boost mode and the alternate transmitter output. + static TX_POWER_MODE_ALTERNATE = 0x02 +} + + +export class EzspPolicyId extends basic.uint8_t { + // Identifies a policy. + + // Controls trust center behavior. + static TRUST_CENTER_POLICY = 0x00 + // Controls how external binding modification requests are handled. + static BINDING_MODIFICATION_POLICY = 0x01 + // Controls whether the Host supplies unicast replies. + static UNICAST_REPLIES_POLICY = 0x02 + // Controls whether pollHandler callbacks are generated. + static POLL_HANDLER_POLICY = 0x03 + // Controls whether the message contents are included in the + // messageSentHandler callback. + static MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04 + // Controls whether the Trust Center will respond to Trust Center link key + // requests. + static TC_KEY_REQUEST_POLICY = 0x05 + // Controls whether the Trust Center will respond to application link key + // requests. + static 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. + static PACKET_VALIDATE_LIBRARY_POLICY = 0x07 + // Controls whether the stack will process ZLL messages. + static ZLL_POLICY = 0x08 +} + + +export class EzspDecisionId extends basic.uint8_t { + // Identifies a policy decision. + + // Send the network key in the clear to all joining and rejoining devices. + static ALLOW_JOINS = 0x00 + // Send the network key in the clear to all joining devices. Rejoining + // devices are sent the network key encrypted with their trust center link + // key. The trust center and any rejoining device are assumed to share a + // link key, either preconfigured or obtained under a previous policy. + static ALLOW_JOINS_REJOINS_HAVE_LINK_KEY = 0x04 + // Send the network key encrypted with the joining or rejoining device's + // trust center link key. The trust center and any joining or rejoining + // device are assumed to share a link key, either preconfigured or obtained + // under a previous policy. This is the default value for the + // TRUST_CENTER_POLICY. + static ALLOW_PRECONFIGURED_KEY_JOINS = 0x01 + // Send the network key encrypted with the rejoining device's trust center + // link key. The trust center and any rejoining device are assumed to share + // a link key, either preconfigured or obtained under a previous policy. No + // new devices are allowed to join. + static ALLOW_REJOINS_ONLY = 0x02 + // Reject all unsecured join and rejoin attempts. + static DISALLOW_ALL_JOINS_AND_REJOINS = 0x03 + // Take no action on trust center rejoin attempts. + static IGNORE_TRUST_CENTER_REJOINS = 0x05 + // BINDING_MODIFICATION_POLICY default decision. Do not allow the local + // binding table to be changed by remote nodes. + static DISALLOW_BINDING_MODIFICATION = 0x10 + // BINDING_MODIFICATION_POLICY decision. Allow remote nodes to change + // the local binding table. + static 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. + static 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. + static 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. + static HOST_WILL_SUPPLY_REPLY = 0x21 + // POLL_HANDLER_POLICY default decision. Do not inform the Host when a child + // polls. + static POLL_HANDLER_IGNORE = 0x30 + // POLL_HANDLER_POLICY decision. Generate a pollHandler callback when a + // child polls. + static POLL_HANDLER_CALLBACK = 0x31 + // MESSAGE_CONTENTS_IN_CALLBACK_POLICY default decision. Include only the + // message tag in the messageSentHandler callback. + static 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. + static 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. + static 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. + static ALLOW_TC_KEY_REQUESTS = 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. + static GENERATE_NEW_TC_LINK_KEY = 0x52 + // APP_KEY_REQUEST_POLICY decision. When the Trust Center receives a request + // for an application link key, it will be ignored. + static 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. + static ALLOW_APP_KEY_REQUESTS = 0x61 + // Indicates that packet validate library checks are enabled on the NCP. + static PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62 + // Indicates that packet validate library checks are NOT enabled on the NCP. + static PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 +} + + +export class EzspMfgTokenId extends basic.uint8_t { + // Manufacturing token IDs used by ezspGetMfgToken(). + + // Custom version (2 bytes). + static MFG_CUSTOM_VERSION = 0x00 + // Manufacturing string (16 bytes). + static MFG_STRING = 0x01 + // Board name (16 bytes). + static MFG_BOARD_NAME = 0x02 + // Manufacturing ID (2 bytes). + static MFG_MANUF_ID = 0x03 + // Radio configuration (2 bytes). + static MFG_PHY_CONFIG = 0x04 + // Bootload AES key (16 bytes). + static MFG_BOOTLOAD_AES_KEY = 0x05 + // ASH configuration (40 bytes). + static MFG_ASH_CONFIG = 0x06 + // EZSP storage (8 bytes). + static MFG_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. + static STACK_CAL_DATA = 0x08 + // Certificate Based Key Exchange (CBKE) data (92 bytes). + static MFG_CBKE_DATA = 0x09 + // Installation code (20 bytes). + static MFG_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. + static STACK_CAL_FILTER = 0x0B + // Custom EUI64 MAC address (8 bytes). + static MFG_CUSTOM_EUI_64 = 0x0C + // CTUNE value (2 byte). + static MFG_CTUNE = 0x0D +} + +export class EzspStatus extends basic.uint8_t { + // Status values used by EZSP. + + // Success. + static SUCCESS = 0x00 + // Fatal error. + static SPI_ERR_FATAL = 0x10 + // The Response frame of the current transaction indicates the NCP has + // reset. + static 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). + static SPI_ERR_OVERSIZED_FRAME = 0x12 + // The Response frame of the current transaction indicates the previous + // transaction was aborted (nSSEL deasserted too soon). + static SPI_ERR_ABORTED_TRANSACTION = 0x13 + // The Response frame of the current transaction indicates the frame + // terminator is missing from the Command frame. + static SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14 + // The NCP has not provided a Response within the time limit defined by + // WAIT_SECTION_TIMEOUT. + static SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15 + // The Response frame from the NCP is missing the frame terminator. + static 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. + static SPI_ERR_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. + static SPI_ERR_RESPONSE_OVERSIZED = 0x18 + // The Host has sent the Command and is still waiting for the NCP to send a + // Response. + static SPI_WAITING_FOR_RESPONSE = 0x19 + // The NCP has not asserted nHOST_INT within the time limit defined by + // WAKE_HANDSHAKE_TIMEOUT. + static SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A + // The NCP has not asserted nHOST_INT after an NCP reset within the time + // limit defined by STARTUP_TIMEOUT. + static SPI_ERR_STARTUP_TIMEOUT = 0x1B + // The Host attempted to verify the SPI Protocol activity and version + // number, and the verification failed. + static 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. + static SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D + // Operation not yet complete. + static ASH_IN_PROGRESS = 0x20 + // Fatal error detected by host. + static HOST_FATAL_ERROR = 0x21 + // Fatal error detected by NCP. + static ASH_NCP_FATAL_ERROR = 0x22 + // Tried to send DATA frame too long. + static DATA_FRAME_TOO_LONG = 0x23 + // Tried to send DATA frame too short. + static DATA_FRAME_TOO_SHORT = 0x24 + // No space for tx'ed DATA frame. + static NO_TX_SPACE = 0x25 + // No space for rec'd DATA frame. + static NO_RX_SPACE = 0x26 + // No receive data available. + static NO_RX_DATA = 0x27 + // Not in Connected state. + static NOT_CONNECTED = 0x28 + // The NCP received a command before the EZSP version had been set. + static ERROR_VERSION_NOT_SET = 0x30 + // The NCP received a command containing an unsupported frame ID. + static ERROR_INVALID_FRAME_ID = 0x31 + // The direction flag in the frame control field was incorrect. + static 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. + static 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. + static ERROR_OVERFLOW = 0x34 + // Insufficient memory was available. + static ERROR_OUT_OF_MEMORY = 0x35 + // The value was out of bounds. + static ERROR_INVALID_VALUE = 0x36 + // The configuration id was not recognized. + static ERROR_INVALID_ID = 0x37 + // Configuration values can no longer be modified. + static ERROR_INVALID_CALL = 0x38 + // The NCP failed to respond to a command. + static ERROR_NO_RESPONSE = 0x39 + // The length of the command exceeded the maximum EZSP frame length. + static ERROR_COMMAND_TOO_LONG = 0x40 + // The UART receive queue was full causing a callback response to be + // dropped. + static ERROR_QUEUE_FULL = 0x41 + // The command has been filtered out by NCP. + static ERROR_COMMAND_FILTERED = 0x42 + // EZSP Security Key is already set + static ERROR_SECURITY_KEY_ALREADY_SET = 0x43 + // EZSP Security Type is invalid + static ERROR_SECURITY_TYPE_INVALID = 0x44 + // EZSP Security Parameters are invalid + static ERROR_SECURITY_PARAMETERS_INVALID = 0x45 + // EZSP Security Parameters are already set + static ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46 + // EZSP Security Key is not set + static ERROR_SECURITY_KEY_NOT_SET = 0x47 + // EZSP Security Parameters are not set + static ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48 + // Received frame with unsupported control byte + static ERROR_UNSUPPORTED_CONTROL = 0x49 + // Received frame is unsecure, when security is established + static ERROR_UNSECURE_FRAME = 0x4A + // Incompatible ASH version + static ASH_ERROR_VERSION = 0x50 + // Exceeded max ACK timeouts + static ASH_ERROR_TIMEOUTS = 0x51 + // Timed out waiting for RSTACK + static ASH_ERROR_RESET_FAIL = 0x52 + // Unexpected ncp reset + static ASH_ERROR_NCP_RESET = 0x53 + // Serial port initialization failed + static ERROR_SERIAL_INIT = 0x54 + // Invalid ncp processor type + static ASH_ERROR_NCP_TYPE = 0x55 + // Invalid ncp reset method + static ASH_ERROR_RESET_METHOD = 0x56 + // XON/XOFF not supported by host driver + static ASH_ERROR_XON_XOFF = 0x57 + // ASH protocol started + static ASH_STARTED = 0x70 + // ASH protocol connected + static ASH_CONNECTED = 0x71 + // ASH protocol disconnected + static ASH_DISCONNECTED = 0x72 + // Timer expired waiting for ack + static ASH_ACK_TIMEOUT = 0x73 + // Frame in progress cancelled + static ASH_CANCELLED = 0x74 + // Received frame out of sequence + static ASH_OUT_OF_SEQUENCE = 0x75 + // Received frame with CRC error + static ASH_BAD_CRC = 0x76 + // Received frame with comm error + static ASH_COMM_ERROR = 0x77 + // Received frame with bad ackNum + static ASH_BAD_ACKNUM = 0x78 + // Received frame shorter than minimum + static ASH_TOO_SHORT = 0x79 + // Received frame longer than maximum + static ASH_TOO_LONG = 0x7A + // Received frame with illegal control byte + static ASH_BAD_CONTROL = 0x7B + // Received frame with illegal length for its type + static ASH_BAD_LENGTH = 0x7C + // Received ASH Ack + static ASH_ACK_RECEIVED = 0x7D + // Sent ASH Ack + static ASH_ACK_SENT = 0x7E + // No reset or error + static NO_ERROR = 0xFF +} + +export class EmberStatus extends basic.uint8_t { + // Return type for stack functions. + + // The generic 'no error' message. + static SUCCESS = 0x00 + // The generic 'fatal error' message. + static ERR_FATAL = 0x01 + // An invalid value was passed as an argument to a function + static BAD_ARGUMENT = 0x02 + // The manufacturing and stack token format in nonvolatile memory is + // different than what the stack expects (returned at initialization). + static EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04 + // The static memory definitions in ember-staticmemory.h are incompatible + // with this stack version. + static INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS = 0x05 + // The manufacturing token format in non-volatile memory is different than + // what the stack expects (returned at initialization). + static EEPROM_MFG_VERSION_MISMATCH = 0x06 + // The stack token format in non-volatile memory is different than what the + // stack expects (returned at initialization). + static EEPROM_STACK_VERSION_MISMATCH = 0x07 + // There are no more buffers. + static NO_BUFFERS = 0x18 + // Specified an invalid baud rate. + static SERIAL_INVALID_BAUD_RATE = 0x20 + // Specified an invalid serial port. + static SERIAL_INVALID_PORT = 0x21 + // Tried to send too much data. + static SERIAL_TX_OVERFLOW = 0x22 + // There was not enough space to store a received character and the + // character was dropped. + static SERIAL_RX_OVERFLOW = 0x23 + // Detected a UART framing error. + static SERIAL_RX_FRAME_ERROR = 0x24 + // Detected a UART parity error. + static SERIAL_RX_PARITY_ERROR = 0x25 + // There is no received data to process. + static SERIAL_RX_EMPTY = 0x26 + // The receive interrupt was not handled in time, and a character was + // dropped. + static SERIAL_RX_OVERRUN_ERROR = 0x27 + // The MAC transmit queue is full. + static MAC_TRANSMIT_QUEUE_FULL = 0x39 + // MAC header FCR error on receive. + static MAC_UNKNOWN_HEADER_TYPE = 0x3A + // The MAC can't complete this task because it is scanning. + static MAC_SCANNING = 0x3D + // No pending data exists for device doing a data poll. + static MAC_NO_DATA = 0x31 + // Attempt to scan when we are joined to a network. + static MAC_JOINED_NETWORK = 0x32 + // Scan duration must be 0 to 14 inclusive. Attempt was made to scan with an + // incorrect duration value. + static MAC_BAD_SCAN_DURATION = 0x33 + // emberStartScan was called with an incorrect scan type. + static MAC_INCORRECT_SCAN_TYPE = 0x34 + // emberStartScan was called with an invalid channel mask. + static MAC_INVALID_CHANNEL_MASK = 0x35 + // Failed to scan current channel because we were unable to transmit the + // relevant MAC command. + static MAC_COMMAND_TRANSMIT_FAILURE = 0x36 + // We expected to receive an ACK following the transmission, but the MAC + // level ACK was never received. + static MAC_NO_ACK_RECEIVED = 0x40 + // Indirect data message timed out before polled. + static MAC_INDIRECT_TIMEOUT = 0x42 + // The Simulated EEPROM is telling the application that there is 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. + static SIM_EEPROM_ERASE_PAGE_GREEN = 0x43 + // The Simulated EEPROM is telling the application that there is at least + // one flash page to be erased. The RED status means the current page has + // filled above the ERASE_CRITICAL_THRESHOLD. Due to the shrinking + // availability of write space, there is a danger of data loss. The + // application must call the function halSimEepromErasePage as soon as + // possible to erase a page. + static SIM_EEPROM_ERASE_PAGE_RED = 0x44 + // The Simulated EEPROM has run out of room to write any 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. + static SIM_EEPROM_FULL = 0x45 + // 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. + static ERR_FLASH_WRITE_INHIBITED = 0x46 + // A fatal error has occurred while trying to write data to the Flash and + // the write verification has failed. The 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. + static ERR_FLASH_VERIFY_FAILED = 0x47 + // Attempt 1 to initialize the Simulated EEPROM has failed. This failure + // means the information already stored in Flash (or a lack thereof), is + // fatally incompatible with the token information compiled into the code + // image being run. + static 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. + static 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. + static SIM_EEPROM_INIT_3_FAILED = 0x4A + // A fatal error has occurred while trying to write data to the flash, + // possibly due to write protection or an invalid address. The 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. + static ERR_FLASH_PROG_FAIL = 0x4B + // A fatal error has occurred while trying to erase flash, possibly due to + // write protection. The 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. + static ERR_FLASH_ERASE_FAIL = 0x4C + // The bootloader received an invalid message (failed attempt to go into + // bootloader). + static ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58 + // Bootloader received an invalid message (failed attempt to go into + // bootloader). + static 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. + static ERR_BOOTLOADER_NO_IMAGE = 0x5A + // The APS layer attempted to send or deliver a message, but it failed. + static DELIVERY_FAILED = 0x66 + // This binding index is out of range of the current binding table. + static BINDING_INDEX_OUT_OF_RANGE = 0x69 + // This address table index is out of range for the current address table. + static ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A + // An invalid binding table index was given to a function. + static INVALID_BINDING_INDEX = 0x6C + // The API call is not allowed given the current state of the stack. + static INVALID_CALL = 0x70 + // The link cost to a node is not known. + static COST_NOT_KNOWN = 0x71 + // The maximum number of in-flight messages (i.e. + // APS_UNICAST_MESSAGE_COUNT) has been reached. + static MAX_MESSAGE_LIMIT_REACHED = 0x72 + // The message to be transmitted is too big to fit into a single over-the- + // air packet. + static MESSAGE_TOO_LONG = 0x74 + // The application is trying to delete or overwrite a binding that is in + // use. + static BINDING_IS_ACTIVE = 0x75 + // The application is trying to overwrite an address table entry that is in + // use. + static ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76 + // Conversion is complete. + static ADC_CONVERSION_DONE = 0x80 + // Conversion cannot be done because a request is being processed. + static ADC_CONVERSION_BUSY = 0x81 + // Conversion is deferred until the current request has been processed. + static ADC_CONVERSION_DEFERRED = 0x82 + // No results are pending. + static ADC_NO_CONVERSION_PENDING = 0x84 + // Sleeping (for a duration) has been abnormally interrupted and exited + // prematurely. + static SLEEP_INTERRUPTED = 0x85 + // The transmit hardware buffer underflowed. + static PHY_TX_UNDERFLOW = 0x88 + // The transmit hardware did not finish transmitting a packet. + static PHY_TX_INCOMPLETE = 0x89 + // An unsupported channel setting was specified. + static PHY_INVALID_CHANNEL = 0x8A + // An unsupported power setting was specified. + static PHY_INVALID_POWER = 0x8B + // The packet cannot be transmitted because the physical MAC layer is + // currently transmitting a packet. (This is used for the MAC backoff + // algorithm.) PHY_TX_CCA_FAIL 0x8D The transmit attempt failed because all + // CCA attempts indicated that the channel was busy + static PHY_TX_BUSY = 0x8C + // The software installed on the hardware doesn't recognize the hardware + // radio type. + static PHY_OSCILLATOR_CHECK_FAILED = 0x8E + // The expected ACK was received after the last transmission. + static PHY_ACK_RECEIVED = 0x8F + // The stack software has completed initialization and is ready to send and + // receive packets over the air. + static NETWORK_UP = 0x90 + // The network is not operating. + static NETWORK_DOWN = 0x91 + // An attempt to join a network failed. + static JOIN_FAILED = 0x94 + // After moving, a mobile node's attempt to re-establish contact with the + // network failed. + static 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. + static CANNOT_JOIN_AS_ROUTER = 0x98 + // The local node ID has changed. The application can obtain the new node ID + // by calling emberGetNodeId(). + static NODE_ID_CHANGED = 0x99 + // The local PAN ID has changed. The application can obtain the new PAN ID + // by calling emberGetPanId(). + static PAN_ID_CHANGED = 0x9A + // An attempt to join or rejoin the network failed because no router beacons + // could be heard by the joining node. + static 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. + static RECEIVED_KEY_IN_THE_CLEAR = 0xAC + // An attempt was made to join a Secured Network, but the device did not + // receive a Network Key. + static NO_NETWORK_KEY_RECEIVED = 0xAD + // After a device joined a Secured Network, a Link Key was requested but no + // response was ever received. + static 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. + static PRECONFIGURED_KEY_REQUIRED = 0xAF + // The node has not joined a network. + static NOT_JOINED = 0x93 + // The chosen security level (the value of SECURITY_LEVEL) is not supported + // by the stack. + static INVALID_SECURITY_LEVEL = 0x95 + // A message cannot be sent because the network is currently overloaded. + static NETWORK_BUSY = 0xA1 + // The application tried to send a message using an endpoint that it has not + // defined. + static 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. + static BINDING_HAS_CHANGED = 0xA4 + // An attempt to generate random bytes failed because of insufficient random + // data from the radio. + static INSUFFICIENT_RANDOM_DATA = 0xA5 + // There was an error in trying to encrypt at the APS Level. This could + // result from either an inability to determine the long address of the + // recipient 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. + // TRUST_CENTER_MASTER_KEY_NOT_SET 0xA7 There was an attempt to form a + // network using commercial security without setting the Trust Center master + // key first. + static APS_ENCRYPTION_ERROR = 0xA6 + // There was an attempt to form or join a network with security without + // calling emberSetInitialSecurityState() first. + static SECURITY_STATE_NOT_SET = 0xA8 + // There was an attempt to set an entry in the key table using an invalid + // long address. An entry cannot be set using either the local device's or + // Trust Center's IEEE address. Or an entry already exists in the table with + // the same IEEE address. An Address of all zeros or all F's are not valid + // addresses in 802.15.4. + static KEY_TABLE_INVALID_ADDRESS = 0xB3 + // There was an attempt to set a security configuration that is not valid + // given the other security settings. + static 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. + static TOO_SOON_FOR_SWITCH_KEY = 0xB8 + // 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). + static KEY_NOT_AUTHORIZED = 0xBB + // The security data provided was not valid, or an integrity check failed. + static SECURITY_DATA_INVALID = 0xBD + // A ZigBee route error command frame was received indicating that a source + // routed message from this node failed en route. + static 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. + static MANY_TO_ONE_ROUTE_FAILURE = 0xAA + // A critical and fatal error indicating that the version of the stack + // trying to run does not match with the chip it is running on. The software + // (stack) on the chip must be replaced with software that is compatible + // with the chip. + static STACK_AND_HARDWARE_MISMATCH = 0xB0 + // An index was passed into the function that was larger than the valid + // range. + static INDEX_OUT_OF_RANGE = 0xB1 + // There are no empty entries left in the table. + static TABLE_FULL = 0xB4 + // The requested table entry has been erased and contains no valid data. + static TABLE_ENTRY_ERASED = 0xB6 + // The requested function cannot be executed because the library that + // contains the necessary functionality is not present. + static LIBRARY_NOT_PRESENT = 0xB5 + // The stack accepted the command and is currently processing the request. + // The results will be returned via an appropriate handler. + static OPERATION_IN_PROGRESS = 0xBA + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_0 = 0xF0 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_1 = 0xF1 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_2 = 0xF2 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_3 = 0xF3 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_4 = 0xF4 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_5 = 0xF5 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_6 = 0xF6 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_7 = 0xF7 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_8 = 0xF8 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_9 = 0xF9 + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_10 = 0xFA + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_11 = 0xFB + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_12 = 0xFC + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_13 = 0xFD + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_14 = 0xFE + // This error is reserved for customer application use. This will never be + // returned from any portion of the network stack or HAL. + static APPLICATION_ERROR_15 = 0xFF +} + + +export class EmberEventUnits extends basic.uint8_t { + // Either marks an event as inactive or specifies the units for the event + // execution time. + + // The event is not scheduled to run. + static EVENT_INACTIVE = 0x00 + // The execution time is in approximate milliseconds. + static EVENT_MS_TIME = 0x01 + // The execution time is in 'binary' quarter seconds (256 approximate + // milliseconds each). + static EVENT_QS_TIME = 0x02 + // The execution time is in 'binary' minutes (65536 approximate milliseconds + // each). + static EVENT_MINUTE_TIME = 0x03 +} + + +export class EmberNodeType extends basic.uint8_t { + // The type of the node. + + // Device is not joined. + static UNKNOWN_DEVICE = 0x00 + // Will relay messages and can act as a parent to other nodes. + static COORDINATOR = 0x01 + // Will relay messages and can act as a parent to other nodes. + static ROUTER = 0x02 + // Communicates only with its parent and will not relay messages. + static END_DEVICE = 0x03 + // An end device whose radio can be turned off to save power. The + // application must poll to receive messages. + static SLEEPY_END_DEVICE = 0x04 + // A sleepy end device that can move through the network. + static MOBILE_END_DEVICE = 0x05 +} + + +export class EmberNetworkStatus extends basic.uint8_t { + // The possible join states for a node. + + // The node is not associated with a network in any way. + static NO_NETWORK = 0x00 + // The node is currently attempting to join a network. + static JOINING_NETWORK = 0x01 + // The node is joined to a network. + static JOINED_NETWORK = 0x02 + // The node is an end device joined to a network but its parent is not + // responding. + static JOINED_NETWORK_NO_PARENT = 0x03 + // The node is in the process of leaving its current network. + static LEAVING_NETWORK = 0x04 +} + + +export class EmberIncomingMessageType extends basic.uint8_t { + // Incoming message types. + + // Unicast. + static INCOMING_UNICAST = 0x00 + // Unicast reply. + static INCOMING_UNICAST_REPLY = 0x01 + // Multicast. + static INCOMING_MULTICAST = 0x02 + // Multicast sent by the local device. + static INCOMING_MULTICAST_LOOPBACK = 0x03 + // Broadcast. + static INCOMING_BROADCAST = 0x04 + // Broadcast sent by the local device. + static INCOMING_BROADCAST_LOOPBACK = 0x05 + // Many to one route request. + static INCOMING_MANY_TO_ONE_ROUTE_REQUEST = 0x06 +} + + +export class EmberOutgoingMessageType extends basic.uint8_t { + // Outgoing message types. + + // Unicast sent directly to an EmberNodeId. + static OUTGOING_DIRECT = 0x00 + // Unicast sent using an entry in the address table. + static OUTGOING_VIA_ADDRESS_TABLE = 0x01 + // Unicast sent using an entry in the binding table. + static OUTGOING_VIA_BINDING = 0x02 + // Multicast message. This value is passed to emberMessageSentHandler() + // only. It may not be passed to emberSendUnicast(). + static OUTGOING_MULTICAST = 0x03 + // Broadcast message. This value is passed to emberMessageSentHandler() + // only. It may not be passed to emberSendUnicast(). + static OUTGOING_BROADCAST = 0x04 +} + + +export class EmberMacPassthroughType extends basic.uint8_t { + // MAC passthrough message type flags. + + // No MAC passthrough messages. + static MAC_PASSTHROUGH_NONE = 0x00 + // SE InterPAN messages. + static MAC_PASSTHROUGH_SE_INTERPAN = 0x01 + // Legacy EmberNet messages. + static MAC_PASSTHROUGH_EMBERNET = 0x02 + // Legacy EmberNet messages filtered by their source address. + static MAC_PASSTHROUGH_EMBERNET_SOURCE = 0x04 + static MAC_PASSTHROUGH_APPLICATION = 0x08 + static MAC_PASSTHROUGH_CUSTOM = 0x10 + static MAC_PASSTHROUGH_INTERNAL = 0x80 +} + + +export class EmberBindingType extends basic.uint8_t { + // Binding types. + + // A binding that is currently not in use. + static UNUSED_BINDING = 0x00 + // A unicast binding whose 64-bit identifier is the destination EUI64. + static UNICAST_BINDING = 0x01 + // A unicast binding whose 64-bit identifier is the aggregator EUI64. + static MANY_TO_ONE_BINDING = 0x02 + // A multicast binding whose 64-bit identifier is the group address. A + // multicast binding can be used to send messages to the group and to + // receive messages sent to the group. + static MULTICAST_BINDING = 0x03 +} + + +export class EmberApsOption extends basic.uint16_t { + // Options to use when sending a message. + + // No options. + static APS_OPTION_NONE = 0x0000 + // UNKNOWN: Discovered while receiving data + static APS_OPTION_UNKNOWN = 0x0008 + // Send the message using APS Encryption, using the Link Key shared with the + // destination node to encrypt the data at the APS Level. + static APS_OPTION_ENCRYPTION = 0x0020 + // Resend the message using the APS retry mechanism. + static APS_OPTION_RETRY = 0x0040 + // Causes a route discovery to be initiated if no route to the destination + // is known. + static APS_OPTION_ENABLE_ROUTE_DISCOVERY = 0x0100 + // Causes a route discovery to be initiated even if one is known. + static APS_OPTION_FORCE_ROUTE_DISCOVERY = 0x0200 + // Include the source EUI64 in the network frame. + static APS_OPTION_SOURCE_EUI64 = 0x0400 + // Include the destination EUI64 in the network frame. + static APS_OPTION_DESTINATION_EUI64 = 0x0800 + // Send a ZDO request to discover the node ID of the destination, if it is + // not already know. + static APS_OPTION_ENABLE_ADDRESS_DISCOVERY = 0x1000 + // Reserved. + static APS_OPTION_POLL_RESPONSE = 0x2000 + // This incoming message is a ZDO request not handled by the EmberZNet + // stack, and the application is responsible for sending a ZDO response. + // This flag is used only when the ZDO is configured to have requests + // handled by the application. See the CONFIG_APPLICATION_ZDO_FLAGS + // configuration parameter for more information. + static APS_OPTION_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. + static APS_OPTION_FRAGMENT = 0x8000 +} + + +export class EzspNetworkScanType extends basic.uint8_t { + // Network scan types. + + // An energy scan scans each channel for its RSSI value. + static ENERGY_SCAN = 0x00 + // An active scan scans each channel for available networks. + static ACTIVE_SCAN = 0x01 +} + + +export class EmberJoinDecision extends basic.uint8_t { + // Decision made by the trust center when a node attempts to join. + + // Allow the node to join. The joining node should have a pre-configured + // key. The security data sent to it will be encrypted with that key. + static USE_PRECONFIGURED_KEY = 0x00 + // Allow the node to join. Send the necessary key (the Network Key in + // Standard Security mode, the Trust Center Master in High Security mode) + // in-the-clear to the joining device. + static SEND_KEY_IN_THE_CLEAR = 0x01 + // Deny join. + static DENY_JOIN = 0x02 + // Take no action. + static NO_ACTION = 0x03 +} + + +export class EmberInitialSecurityBitmask extends basic.uint16_t { + // This is the Initial Security Bitmask that controls the use of various + // security features. + + // This enables ZigBee Standard Security on the node. + static STANDARD_SECURITY_MODE = 0x0000 + // This enables Distributed Trust Center Mode for the device forming the + // network. (Previously known as NO_TRUST_CENTER_MODE) + static DISTRIBUTED_TRUST_CENTER_MODE = 0x0002 + // This enables a Global Link Key for the Trust Center. All nodes will share + // the same Trust Center Link Key. + static TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004 + // This enables devices that perform MAC Association with a pre-configured + // Network Key to join the network. It is only set on the Trust Center. + static PRECONFIGURED_NETWORK_KEY_MODE = 0x0008 + // This denotes that the preconfiguredKey is not the actual Link Key but a + // Secret Key known only to the Trust Center. It is hashed with the IEEE + // Address of the destination device in order 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. + static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084 + // This denotes that the preconfiguredKey element has valid data that should + // be used to configure the initial security state. + static HAVE_PRECONFIGURED_KEY = 0x0100 + // This denotes that the networkKey element has valid data that should be + // used to configure the initial security state. + static 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 only necessary if the device does + // not have a pre-configured key. + static 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 preconfigured 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 utilizing a pre-configured key. + static 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 in cases where a device is joining to the same + // network a again (but not using ::emberRejoinNetwork()) it should keep the + // NWK and APS frame counters stored in its tokens. + static NO_FRAME_COUNTER_RESET = 0x1000 + // This denotes that the device should obtain its preconfigured key from an + // installation code stored in the manufacturing token. The token contains a + // value that will be hashed to obtain the actual preconfigured key. If that + // token is not valid, then the call to emberSetInitialSecurityState() will + // fail. + static GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000 + // 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, which 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. + static HAVE_TRUST_CENTER_EUI64 = 0x0040 +} + + +export class EmberCurrentSecurityBitmask extends basic.uint16_t { + // This is the Current Security Bitmask that details the use of various + // security features. + + // This denotes that the device is running in a network with ZigBee Standard + // Security. + static STANDARD_SECURITY_MODE = 0x0000 + // This denotes that the device is running in a network with ZigBee High + // Security. + static HIGH_SECURITY_MODE = 0x0001 + // This denotes that the device is running in a network without a + // centralized Trust Center. + static 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. + static GLOBAL_LINK_KEY = 0x0004 + // This denotes that the node has a Trust Center Link Key. + static HAVE_TRUST_CENTER_LINK_KEY = 0x0010 + // This denotes that the Trust Center is using a Hashed Link Key. + static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084 +} + + +export class EmberKeyType extends basic.uint8_t { + // Describes the type of ZigBee security key. + + // A shared key between the Trust Center and a device. + static TRUST_CENTER_LINK_KEY = 0x01 + // A shared secret used for deriving keys between the Trust Center and a + // device + static TRUST_CENTER_MASTER_KEY = 0x02 + // The current active Network Key used by all devices in the network. + static CURRENT_NETWORK_KEY = 0x03 + // The alternate Network Key that was previously in use, or the newer key + // that will be switched to. + static NEXT_NETWORK_KEY = 0x04 + // An Application Link Key shared with another (non-Trust Center) device. + static APPLICATION_LINK_KEY = 0x05 + // An Application Master Key shared secret used to derive an Application + // Link Key. + static APPLICATION_MASTER_KEY = 0x06 +} + +export class EmberKeyStructBitmask extends basic.uint16_t { + // Describes the presence of valid data within the EmberKeyStruct structure. + + // The key has a sequence number associated with it. + static KEY_HAS_SEQUENCE_NUMBER = 0x0001 + // The key has an outgoing frame counter associated with it. + static KEY_HAS_OUTGOING_FRAME_COUNTER = 0x0002 + // The key has an incoming frame counter associated with it. + static KEY_HAS_INCOMING_FRAME_COUNTER = 0x0004 + // The key has a Partner IEEE address associated with it. + static KEY_HAS_PARTNER_EUI64 = 0x0008 +} + + +export class EmberDeviceUpdate extends basic.uint8_t { + // The status of the device update. + + static STANDARD_SECURITY_SECURED_REJOIN = 0x0 + static STANDARD_SECURITY_UNSECURED_JOIN = 0x1 + static DEVICE_LEFT = 0x2 + static STANDARD_SECURITY_UNSECURED_REJOIN = 0x3 + static HIGH_SECURITY_SECURED_REJOIN = 0x4 + static HIGH_SECURITY_UNSECURED_JOIN = 0x5 + static HIGH_SECURITY_UNSECURED_REJOIN = 0x7 +} + + +export class EmberKeyStatus extends basic.uint8_t { + // The status of the attempt to establish a key. + + static APP_LINK_KEY_ESTABLISHED = 0x01 + static APP_MASTER_KEY_ESTABLISHED = 0x02 + static TRUST_CENTER_LINK_KEY_ESTABLISHED = 0x03 + static KEY_ESTABLISHMENT_TIMEOUT = 0x04 + static KEY_TABLE_FULL = 0x05 + static TC_RESPONDED_TO_KEY_REQUEST = 0x06 + static TC_APP_KEY_SENT_TO_REQUESTER = 0x07 + static TC_RESPONSE_TO_KEY_REQUEST_FAILED = 0x08 + static TC_REQUEST_KEY_TYPE_NOT_SUPPORTED = 0x09 + static TC_NO_LINK_KEY_FOR_REQUESTER = 0x0A + static TC_REQUESTER_EUI64_UNKNOWN = 0x0B + static TC_RECEIVED_FIRST_APP_KEY_REQUEST = 0x0C + static TC_TIMEOUT_WAITING_FOR_SECOND_APP_KEY_REQUEST = 0x0D + static TC_NON_MATCHING_APP_KEY_REQUEST_RECEIVED = 0x0E + static TC_FAILED_TO_SEND_APP_KEYS = 0x0F + static TC_FAILED_TO_STORE_APP_KEY_REQUEST = 0x10 + static TC_REJECTED_APP_KEY_REQUEST = 0x11 +} + +export class EmberCounterType extends basic.uint8_t { + // Defines the events reported to the application by the + // readAndClearCounters command. + + // The MAC received a broadcast. + static COUNTER_MAC_RX_BROADCAST = 0 + // The MAC transmitted a broadcast. + static COUNTER_MAC_TX_BROADCAST = 1 + // The MAC received a unicast. + static COUNTER_MAC_RX_UNICAST = 2 + // The MAC successfully transmitted a unicast. + static COUNTER_MAC_TX_UNICAST_SUCCESS = 3 + // The MAC retried a unicast. + static COUNTER_MAC_TX_UNICAST_RETRY = 4 + // The MAC unsuccessfully transmitted a unicast. + static COUNTER_MAC_TX_UNICAST_FAILED = 5 + // The APS layer received a data broadcast. + static COUNTER_APS_DATA_RX_BROADCAST = 6 + // The APS layer transmitted a data broadcast. + static COUNTER_APS_DATA_TX_BROADCAST = 7 + // The APS layer received a data unicast. + static COUNTER_APS_DATA_RX_UNICAST = 8 + // The APS layer successfully transmitted a data unicast. + static COUNTER_APS_DATA_TX_UNICAST_SUCCESS = 9 + // The APS layer retried a data unicast. + static COUNTER_APS_DATA_TX_UNICAST_RETRY = 10 + // The APS layer unsuccessfully transmitted a data unicast. + static COUNTER_APS_DATA_TX_UNICAST_FAILED = 11 + // The network layer successfully submitted a new route discovery to the + // MAC. + static COUNTER_ROUTE_DISCOVERY_INITIATED = 12 + // An entry was added to the neighbor table. + static COUNTER_NEIGHBOR_ADDED = 13 + // An entry was removed from the neighbor table. + static COUNTER_NEIGHBOR_REMOVED = 14 + // A neighbor table entry became stale because it had not been heard from. + static COUNTER_NEIGHBOR_STALE = 15 + // A node joined or rejoined to the network via this node. + static COUNTER_JOIN_INDICATION = 16 + // An entry was removed from the child table. + static COUNTER_CHILD_REMOVED = 17 + // EZSP-UART only. An overflow error occurred in the UART. + static COUNTER_ASH_OVERFLOW_ERROR = 18 + // EZSP-UART only. A framing error occurred in the UART. + static COUNTER_ASH_FRAMING_ERROR = 19 + // EZSP-UART only. An overrun error occurred in the UART. + static COUNTER_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. + static COUNTER_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. + static COUNTER_APS_FRAME_COUNTER_FAILURE = 22 + // Utility counter for general debugging use. + static COUNTER_UTILITY = 23 + // A message was dropped at the APS layer because it had APS encryption but + // the key associated with the sender has not been authenticated, and thus + // the key is not authorized for use in APS data messages. + static COUNTER_APS_LINK_KEY_NOT_AUTHORIZED = 24 + // A NWK encrypted message was received but dropped because decryption + // failed. + static COUNTER_NWK_DECRYPTION_FAILURE = 25 + // An APS encrypted message was received but dropped because decryption + // failed. + static COUNTER_APS_DECRYPTION_FAILURE = 26 + // The number of times we failed 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. + static COUNTER_ALLOCATE_PACKET_BUFFER_FAILURE = 27 + // The number of relayed unicast packets. + static COUNTER_RELAYED_UNICAST = 28 + // The number of times we dropped a packet due to reaching + // the preset PHY to MAC queue limit (emMaxPhyToMacQueueLength). + static COUNTER_PHY_TO_MAC_QUEUE_LIMIT_REACHED = 29 + // The number of times we dropped a packet due to the + // packet-validate library checking a packet and rejecting it + // due to length or other formatting problems. + static COUNTER_PACKET_VALIDATE_LIBRARY_DROPPED_COUNT = 30 + // The number of times the NWK retry queue is full and a + // new message failed to be added. + static COUNTER_TYPE_NWK_RETRY_OVERFLOW = 31 + // The number of times the PHY layer was unable to transmit + // due to a failed CCA. + static COUNTER_PHY_CCA_FAIL_COUNT = 32 + // The number of times a NWK broadcast was dropped because + // the broadcast table was full. + static COUNTER_BROADCAST_TABLE_FULL = 33 + // The number of low priority packet traffic arbitration requests. + static COUNTER_PTA_LO_PRI_REQUESTED = 34 + // The number of high priority packet traffic arbitration requests. + static COUNTER_PTA_HI_PRI_REQUESTED = 35 + // The number of low priority packet traffic arbitration requests denied. + static COUNTER_PTA_LO_PRI_DENIED = 36 + // The number of high priority packet traffic arbitration requests denied. + static COUNTER_PTA_HI_PRI_DENIED = 37 + // The number of aborted low priority packet traffic arbitration transmissions. + static COUNTER_PTA_LO_PRI_TX_ABORTED = 38 + // The number of aborted high priority packet traffic arbitration transmissions. + static COUNTER_PTA_HI_PRI_TX_ABORTED = 39 + // A placeholder giving the number of Ember counter types. + static COUNTER_TYPE_COUNT = 40 +} + + +export class EmberJoinMethod extends basic.uint8_t { + // The type of method used for joining. + + // Normally devices use MAC Association to join a network, which respects + // the "permit joining" flag in the MAC Beacon. For mobile nodes this value + // causes the device to use an Ember Mobile Node Join, which is functionally + // equivalent to a MAC association. This value should be used by default. + static USE_MAC_ASSOCIATION = 0x0 + // For those networks where the "permit joining" flag is never turned on, + // they 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(). For a mobile node this value will cause + // it to use an Ember Mobile node rejoin, which is functionally equivalent. + static USE_NWK_REJOIN = 0x1 + // For those networks where the "permit joining" flag is never turned on, + // they 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 ::USE_NWK_COMMISSIONING join method + // below. + static USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2 + // For those 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. + static USE_NWK_COMMISSIONING = 0x3 +} + + +export class EmberZdoConfigurationFlags extends basic.uint8_t { + // Flags for controlling which incoming ZDO requests are passed to the + // application. To see if the application is required to send a ZDO response + // to an incoming message, the application must check the APS options + // bitfield within the incomingMessageHandler callback to see if the + // APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set. + + // Set this flag in order to receive supported ZDO request messages via the + // incomingMessageHandler callback. A supported ZDO request is one that is + // handled by the EmberZNet stack. The stack will continue to handle the + // request and send the appropriate ZDO response even if this configuration + // option is enabled. + static APP_RECEIVES_SUPPORTED_ZDO_REQUESTS = 0x01 + // Set this flag in order to receive unsupported ZDO request messages via + // the incomingMessageHandler callback. An unsupported ZDO request is one + // that is not handled by the EmberZNet stack, other than to send a 'not + // supported' ZDO response. If this configuration option is enabled, the + // stack will no longer send any ZDO response, and it is the application's + // responsibility to do so. + static APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS = 0x02 + // Set this flag in order to receive the following ZDO request messages via + // the incomingMessageHandler callback: SIMPLE_DESCRIPTOR_REQUEST, + // MATCH_DESCRIPTORS_REQUEST, and ACTIVE_ENDPOINTS_REQUEST. If this + // configuration option is enabled, the stack will no longer send any ZDO + // response for these requests, and it is the application's responsibility + // to do so. + static APP_HANDLES_ZDO_ENDPOINT_REQUESTS = 0x04 + // Set this flag in order to receive the following ZDO request messages via + // the incomingMessageHandler callback: BINDING_TABLE_REQUEST, BIND_REQUEST, + // and UNBIND_REQUEST. If this configuration option is enabled, the stack + // will no longer send any ZDO response for these requests, and it is the + // application's responsibility to do so. + static APP_HANDLES_ZDO_BINDING_REQUESTS = 0x08 +} + + +export class EmberConcentratorType extends basic.uint16_t { + // Type of concentrator. + + // 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. + static 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. + static HIGH_RAM_CONCENTRATOR = 0xFFF9 +} + + +export class EmberZllState extends basic.uint16_t { + // ZLL device state identifier. + + // No state. + static ZLL_STATE_NONE = 0x0000 + // The device is factory new. + static ZLL_STATE_FACTORY_NEW = 0x0001 + // The device is capable of assigning addresses to other devices. + static ZLL_STATE_ADDRESS_ASSIGNMENT_CAPABLE = 0x0002 + // The device is initiating a link operation. + static ZLL_STATE_LINK_INITIATOR = 0x0010 + // The device is requesting link priority. + static ZLL_STATE_LINK_PRIORITY_REQUEST = 0x0020 + // The device is on a non-ZLL network. + static ZLL_STATE_NON_ZLL_NETWORK = 0x0100 +} + + +export class EmberZllKeyIndex extends basic.uint8_t { + // ZLL key encryption algorithm enumeration. + + // Key encryption algorithm for use during development. + static ZLL_KEY_INDEX_DEVELOPMENT = 0x00 + // Key encryption algorithm shared by all certified devices. + static ZLL_KEY_INDEX_MASTER = 0x04 + // Key encryption algorithm for use during development and certification. + static ZLL_KEY_INDEX_CERTIFICATION = 0x0F +} + +export class EzspZllNetworkOperation extends basic.uint8_t { + // Differentiates among ZLL network operations. + + static ZLL_FORM_NETWORK = 0x00 // ZLL form network command. + static ZLL_JOIN_TARGET = 0x01 // ZLL join target command. +} + + +export class EzspSourceRouteOverheadInformation extends basic.uint8_t { + // Validates Source Route Overhead Information cached. + + // Ezsp source route overhead unknown + static SOURCE_ROUTE_OVERHEAD_UNKNOWN = 0xFF +} + +export class EmberNetworkInitBitmask extends basic.uint16_t { + // Bitmask options for emberNetworkInit(). + + // No options for Network Init + static NETWORK_INIT_NO_OPTIONS = 0x0000 + // Save parent info (node ID and EUI64) in a token during joining/rejoin, + // and restore on reboot. + static NETWORK_INIT_PARENT_INFO_IN_TOKEN = 0x0001 +} \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts new file mode 100644 index 0000000000..1e5df8b586 --- /dev/null +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -0,0 +1,588 @@ +import * as basic from './basic'; +import * as named from './named'; + +export class EzspStruct { + constructor(...args: any[]) { + // if len(args) == 1 and isinstance(args[0], self.__class__): + // # copy constructor + // for field in self._fields: + // setattr(self, field[0], getattr(args[0], field[0])) + } + static serialize(cls: any, obj: any) { + return Buffer.concat(cls._fields.map( (field:any[]) => { + let value = obj[field[0]]; + console.assert(field[1]) + return field[1].serialize(field[1], value); + })) + } + + static deserialize(cls: any, data: Buffer) { + var r = new cls(); + for (let [field_name, field_type] of cls._fields) { + let v; + [v, data] = field_type.deserialize(field_type, data) + r[field_name] = v; + } + return [r, data] + } + + /*toString() { + //r = '<%s ' % (self.__class__.__name__, ) + //r += ' '.join( + // ['%s=%s' % (f[0], getattr(self, f[0], None)) for f in self._fields] + //) + //r += '>' + //return r + }*/ +} + +export class EmberNetworkParameters extends EzspStruct { + static _fields = [ + // The network's extended PAN identifier. + ['extendedPanId', basic.fixed_list(8, basic.uint8_t)], + // The network's PAN identifier. + ['panId', basic.uint16_t], + // A power setting, in dBm. + ['radioTxPower', basic.uint8_t], + // A radio channel. + ['radioChannel', basic.uint8_t], + // The method used to initially join the network. + ['joinMethod', named.EmberJoinMethod], + // NWK Manager ID. The ID of the network manager in the current network. + // This may only be set at joining when using USE_NWK_COMMISSIONING as + // the join method. + ['nwkManagerId', named.EmberNodeId], + // NWK Update ID. The value of the ZigBee nwkUpdateId known by the + // stack. This 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 USE_NWK_COMMISSIONING as the join method. + ['nwkUpdateId', basic.uint8_t], + // 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 USE_NWK_COMMISSIONING as the join + // method. + ['channels', basic.uint32_t], + ] +} +export class EmberZigbeeNetwork extends EzspStruct { + // The parameters of a ZigBee network. + static _fields = [ + // The 802.15.4 channel associated with the network. + ['channel', basic.uint8_t], + // The network's PAN identifier. + ['panId', basic.uint16_t], + // The network's extended PAN identifier. + ['extendedPanId', basic.fixed_list(8, basic.uint8_t)], + // Whether the network is allowing MAC associations. + ['allowingJoin', named.Bool], + // The Stack Profile associated with the network. + ['stackProfile', basic.uint8_t], + // The instance of the Network. + ['nwkUpdateId', basic.uint8_t], + ] +} + +export class EmberApsFrame extends EzspStruct { + + public profileId: number; + public sequence: number; + public clusterId: number; + public sourceEndpoint: number; + public destinationEndpoint: number; + public groupId?: number; + public options?: named.EmberApsOption; + + + // ZigBee APS frame parameters. + static _fields = [ + // The application profile ID that describes the format of the message. + ['profileId', basic.uint16_t], + // The cluster ID for this message. + ['clusterId', basic.uint16_t], + // The source endpoint. + ['sourceEndpoint', basic.uint8_t], + // The destination endpoint. + ['destinationEndpoint', basic.uint8_t], + // A bitmask of options. + ['options', named.EmberApsOption], + // The group ID for this message, if it is multicast mode. + ['groupId', basic.uint16_t], + // The sequence number. + ['sequence', basic.uint8_t], + ] +} + +export class EmberBindingTableEntry extends EzspStruct { + // An entry in the binding table. + static _fields = [ + // The type of binding. + ['type', named.EmberBindingType], + // The endpoint on the local node. + ['local', basic.uint8_t], + // 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 send messages + // with any cluster ID, not just that listed in the binding. + ['clusterId', basic.uint16_t], + // The endpoint on the remote node [specified by identifier]. + ['remote', basic.uint8_t], + // A 64- bit identifier.This is either the destination EUI64 [for + // unicasts] or the 64- bit group address [for multicasts]. + ['identifier', named.EmberEUI64], + // The index of the network the binding belongs to. + ['networkIndex', basic.uint8_t], + ] +} + +export class EmberMulticastTableEntry extends EzspStruct { + // 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. + static _fields = [ + // The multicast group ID. + ['multicastId', named.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.] + ['endpoint', basic.uint8_t], + // The network index of the network the entry is related to. + ['networkIndex', basic.uint8_t], + ] +} + +export class EmberKeyData extends EzspStruct { + // A 128- bit key. + static _fields = [ + // The key data. + ['contents', basic.fixed_list(16, basic.uint8_t)], + ] +} + +export class EmberCertificateData extends EzspStruct { + + // The implicit certificate used in CBKE. + static _fields = [ + // The certificate data. + ['contents', basic.fixed_list(48, basic.uint8_t)], + ] + +} + +export class EmberPublicKeyData extends EzspStruct { + // The public key data used in CBKE. + static _fields = [ + // The public key data. + ['contents', basic.fixed_list(22, basic.uint8_t)], + ] +} + +export class EmberPrivateKeyData extends EzspStruct { + // The private key data used in CBKE. + static _fields = [ + // The private key data. + ['contents', basic.fixed_list(21, basic.uint8_t)], + ] +} + +export class EmberSmacData extends EzspStruct { + // The Shared Message Authentication Code data used in CBKE. + static _fields = [ + // The Shared Message Authentication Code data. + ['contents', basic.fixed_list(16, basic.uint8_t)], + ] +} + +export class EmberSignatureData extends EzspStruct { + // An ECDSA signature + static _fields = [ + // The signature data. + ['contents', basic.fixed_list(42, basic.uint8_t)], + ] +} + +export class EmberCertificate283k1Data extends EzspStruct { + // The implicit certificate used in CBKE. + static _fields = [ + // The 283k1 certificate data. + ['contents', basic.fixed_list(74, basic.uint8_t)], + ] +} + +export class EmberPublicKey283k1Data extends EzspStruct { + // The public key data used in CBKE. + static _fields = [ + // The 283k1 public key data. + ['contents', basic.fixed_list(37, basic.uint8_t)], + ] +} + +export class EmberPrivateKey283k1Data extends EzspStruct { + // The private key data used in CBKE. + static _fields = [ + // The 283k1 private key data. + ['contents', basic.fixed_list(36, basic.uint8_t)], + ] +} + +export class EmberSignature283k1Data extends EzspStruct { + // An ECDSA signature + static _fields = [ + // The 283k1 signature data. + ['contents', basic.fixed_list(72, basic.uint8_t)], + ] +} + +export class EmberMessageDigest extends EzspStruct { + // The calculated digest of a message + static _fields = [ + // The calculated digest of a message. + ['contents', basic.fixed_list(16, basic.uint8_t)], + ] +} + +export class EmberAesMmoHashContext extends EzspStruct { + // The hash context for an ongoing hash operation. + static _fields = [ + // The result of ongoing the hash operation. + ['result', basic.fixed_list(16, basic.uint8_t)], + // The total length of the data that has been hashed so far. + ['length', basic.uint32_t], + ] +} + +export class EmberNeighborTableEntry extends EzspStruct { + // A neighbor table entry stores information about the reliability of RF + // links to and from neighboring nodes. + static _fields = [ + // The neighbor's two byte network id + ['shortId', basic.uint16_t], + // An exponentially weighted moving average of the link quality values + // of incoming packets from this neighbor as reported by the PHY. + ['averageLqi', basic.uint8_t], + // 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. + ['inCost', basic.uint8_t], + // 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. + ['outCost', basic.uint8_t], + // The number of aging periods elapsed since a link status message was + // last received from this neighbor. The aging period is 16 seconds. + ['age', basic.uint8_t], + // The 8 byte EUI64 of the neighbor. + ['longId', named.EmberEUI64], + ] +} + +export class EmberRouteTableEntry extends EzspStruct { + // A route table entry stores information about the next hop along the route + // to the destination. + static _fields = [ + // The short id of the destination. A value of 0xFFFF indicates the + // entry is unused. + ['destination', basic.uint16_t], + // The short id of the next hop to this destination. + ['nextHop', basic.uint16_t], + // Indicates whether this entry is active [0], being discovered [1]], + // unused [3], or validating [4]. + ['status', basic.uint8_t], + // The number of seconds since this route entry was last used to send a + // packet. + ['age', basic.uint8_t], + // Indicates whether this destination is a High RAM Concentrator [2], a + // Low RAM Concentrator [1], or not a concentrator [0]. + ['concentratorType', basic.uint8_t], + // 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. + ['routeRecordState', basic.uint8_t], + ] +} + +export class EmberInitialSecurityState extends EzspStruct { + // The security data used to set the configuration for the stack, or the + // retrieved configuration currently in use. + static _fields = [ + // A bitmask indicating the security state used to indicate what the + // security configuration will be when the device forms or joins the + // network. + ['bitmask', named.EmberInitialSecurityBitmask], + // The pre-configured Key data that should be used when forming or + // joining the network. The security bitmask must be set with the + // HAVE_PRECONFIGURED_KEY bit to indicate that the key contains valid + // data. + ['preconfiguredKey', EmberKeyData], + // The Network Key that should be used by the Trust Center when it forms + // the network, or the Network Key currently in use by a joined device. + // The security bitmask must be set with HAVE_NETWORK_KEY to indicate + // that the key contains valid data. + ['networkKey', EmberKeyData], + // The sequence number associated with the network key. This is only + // valid if the HAVE_NETWORK_KEY has been set in the security bitmask. + ['networkKeySequenceNumber', basic.uint8_t], + // 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 + // instead it is learned during the joining message exchange. This field + // is only examined if 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. + ['preconfiguredTrustCenterEui64', named.EmberEUI64], + ] +} + +export class EmberCurrentSecurityState extends EzspStruct { + // The security options and information currently used by the stack. + static _fields = [ + // A bitmask indicating the security options currently in use by a + // device joined in the network. + ['bitmask', named.EmberCurrentSecurityBitmask], + // The IEEE Address of the Trust Center device. + ['trustCenterLongAddress', named.EmberEUI64], + ] +} + +export class EmberKeyStruct extends EzspStruct { + // A structure containing a key and its associated data. + static _fields = [ + // A bitmask indicating the presence of data within the various fields + // in the structure. + ['bitmask', named.EmberKeyStructBitmask], + // The type of the key. + ['type', named.EmberKeyType], + // The actual key data. + ['key', EmberKeyData], + // The outgoing frame counter associated with the key. + ['outgoingFrameCounter', basic.uint32_t], + // The frame counter of the partner device associated with the key. + ['incomingFrameCounter', basic.uint32_t], + // The sequence number associated with the key. + ['sequenceNumber', basic.uint8_t], + // The IEEE address of the partner device also in possession of the key. + ['partnerEUI64', named.EmberEUI64], + ] +} + +export class EmberNetworkInitStruct extends EzspStruct { + // Network Initialization parameters. + static _fields = [ + // Configuration options for network init. + ['bitmask', named.EmberNetworkInitBitmask], + ] +} + +export class EmberZllSecurityAlgorithmData extends EzspStruct { + // Data associated with the ZLL security algorithm. + static _fields = [ + // Transaction identifier. + ['transactionId', basic.uint32_t], + // Response identifier. + ['responseId', basic.uint32_t], + // Bitmask. + ['bitmask', basic.uint16_t], + ] +} + +export class EmberZllNetwork extends EzspStruct { + // The parameters of a ZLL network. + static _fields = [ + // The parameters of a ZigBee network. + ['zigbeeNetwork', EmberZigbeeNetwork], + // Data associated with the ZLL security algorithm. + ['securityAlgorithm', EmberZllSecurityAlgorithmData], + // Associated EUI64. + ['eui64', named.EmberEUI64], + // The node id. + ['nodeId', named.EmberNodeId], + // The ZLL state. + ['state', named.EmberZllState], + // The node type. + ['nodeType', named.EmberNodeType], + // The number of sub devices. + ['numberSubDevices', basic.uint8_t], + // The total number of group identifiers. + ['totalGroupIdentifiers', basic.uint8_t], + // RSSI correction value. + ['rssiCorrection', basic.uint8_t], + ] +} + +export class EmberZllInitialSecurityState extends EzspStruct { + // Describes the initial security features and requirements that will be + // used when forming or joining ZLL networks. + static _fields = [ + // Unused bitmask; reserved for future use. + ['bitmask', basic.uint32_t], + // The key encryption algorithm advertised by the application. + ['keyIndex', named.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], + ] +} + +export class EmberZllDeviceInfoRecord extends EzspStruct { + // Information about a specific ZLL Device. + static _fields = [ + // EUI64 associated with the device. + ['ieeeAddress', named.EmberEUI64], + // Endpoint id. + ['endpointId', basic.uint8_t], + // Profile id. + ['profileId', basic.uint16_t], + // Device id. + ['deviceId', basic.uint16_t], + // Associated version. + ['version', basic.uint8_t], + // Number of relevant group ids. + ['groupIdCount', basic.uint8_t], + ] +} + +export class EmberZllAddressAssignment extends EzspStruct { + // ZLL address assignment data. + static _fields = [ + // Relevant node id. + ['nodeId', named.EmberNodeId], + // Minimum free node id. + ['freeNodeIdMin', named.EmberNodeId], + // Maximum free node id. + ['freeNodeIdMax', named.EmberNodeId], + // Minimum group id. + ['groupIdMin', named.EmberMulticastId], + // Maximum group id. + ['groupIdMax', named.EmberMulticastId], + // Minimum free group id. + ['freeGroupIdMin', named.EmberMulticastId], + // Maximum free group id. + ['freeGroupIdMax', named.EmberMulticastId], + ] +} + +export class EmberTokTypeStackZllData extends EzspStruct { + // Public API for ZLL stack data token. + static _fields = [ + // Token bitmask. + ['bitmask', basic.uint32_t], + // Minimum free node id. + ['freeNodeIdMin', basic.uint16_t], + // Maximum free node id. + ['freeNodeIdMax', basic.uint16_t], + // Local minimum group id. + ['myGroupIdMin', basic.uint16_t], + // Minimum free group id. + ['freeGroupIdMin', basic.uint16_t], + // Maximum free group id. + ['freeGroupIdMax', basic.uint16_t], + // RSSI correction value. + ['rssiCorrection', basic.uint8_t], + ] +} + +export class EmberTokTypeStackZllSecurity extends EzspStruct { + // Public API for ZLL stack security token. + static _fields = [ + // Token bitmask. + ['bitmask', basic.uint32_t], + // Key index. + ['keyIndex', basic.uint8_t], + // Encryption key. + ['encryptionKey', basic.fixed_list(16, basic.uint8_t)], + // Preconfigured key. + ['preconfiguredKey', basic.fixed_list(16, basic.uint8_t)], + ] +} + +export class EmberRf4ceVendorInfo extends EzspStruct { + // The RF4CE vendor information block. + static _fields = [ + // The vendor identifier field shall contain the vendor identifier of + // the node. + ['vendorId', basic.uint16_t], + // The vendor string field shall contain the vendor string of the node. + ['vendorString', basic.fixed_list(7, basic.uint8_t)], + ] +} + +export class EmberRf4ceApplicationInfo extends EzspStruct { + // The RF4CE application information block. + static _fields = [ + // The application capabilities field shall contain information relating + // to the capabilities of the application of the node. + ['capabilities', named.EmberRf4ceApplicationCapabilities], + // The user string field shall contain the user specified identification + // string. + ['userString', basic.fixed_list(15, basic.uint8_t)], + // The device type list field shall contain the list of device types + // supported by the node. + ['deviceTypeList', basic.fixed_list(3, basic.uint8_t)], + // The profile ID list field shall contain the list of profile + // identifiers disclosed as supported by the node. + ['profileIdList', basic.fixed_list(7, basic.uint8_t)], + ] +} + +export class EmberRf4cePairingTableEntry extends EzspStruct { + // The internal representation of an RF4CE pairing table entry. + static _fields = [ + // The link key to be used to secure this pairing link. + ['securityLinkKey', EmberKeyData], + // The IEEE address of the destination device. + ['destLongId', named.EmberEUI64], + // The frame counter last received from the recipient node. + ['frameCounter', basic.uint32_t], + // The network address to be assumed by the source device. + ['sourceNodeId', named.EmberNodeId], + // The PAN identifier of the destination device. + ['destPanId', named.EmberPanId], + // The network address of the destination device. + ['destNodeId', named.EmberNodeId], + // The vendor ID of the destination device. + ['destVendorId', basic.uint16_t], + // The list of profiles supported by the destination device. + ['destProfileIdList', basic.fixed_list(7, basic.uint8_t)], + // The length of the list of supported profiles. + ['destProfileIdListLength', basic.uint8_t], + // Info byte. + ['info', basic.uint8_t], + // The expected channel of the destination device. + ['channel', basic.uint8_t], + // The node capabilities of the recipient node. + ['capabilities', basic.uint8_t], + // Last MAC sequence number seen on this pairing link. + ['lastSeqn', basic.uint8_t], + ] +} + +export class EmberGpAddress extends EzspStruct { + // A GP address structure. + static _fields = [ + // The GPD's EUI64. + ['gpdIeeeAddress', named.EmberEUI64], + // The GPD's source ID. + ['sourceId', basic.uint32_t], + // The GPD Application ID. + ['applicationId', basic.uint8_t], + // The GPD endpoint. + ['endpoint', basic.uint8_t], + ] +} + +export class EmberGpSinkListEntry extends EzspStruct { + // A sink list entry + static _fields = [ + // The sink list type. + ['type', basic.uint8_t], + // The EUI64 of the target sink. + ['sinkEUI', named.EmberEUI64], + // The short address of the target sink. + ['sinkNodeId', named.EmberNodeId], + ] +} + + diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts new file mode 100644 index 0000000000..e22188478e --- /dev/null +++ b/src/adapter/ezsp/driver/uart.ts @@ -0,0 +1,384 @@ +import * as SerialPort from 'serialport' +import { Deferred, AsyncQueue, crc16ccitt } from './utils'; + +const FLAG = 0x7E // Marks end of frame +const ESCAPE = 0x7D +const XON = 0x11 // Resume transmission +const XOFF = 0x13 // Stop transmission +const SUBSTITUTE = 0x18 +const CANCEL = 0x1A // Terminates a frame in progress + +const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] + +class Terminator { +} + +enum NcpResetCode { + RESET_UNKNOWN_REASON = 0x00, + RESET_EXTERNAL = 0x01, + RESET_POWER_ON = 0x02, + RESET_WATCHDOG = 0x03, + RESET_ASSERT = 0x06, + RESET_BOOTLOADER = 0x09, + RESET_SOFTWARE = 0x0B, + ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, + ERROR_UNKNOWN_EM3XX_ERROR = 0x80, +} + +export class UartProtocol implements AsyncIterable { + + _send_seq = 0; + _rec_seq = 0; + _buffer: Buffer | undefined = Buffer.alloc(256); + _reset_deferred: Deferred; + _pending: any; + _sendq: AsyncQueue<{ data: Buffer, seq: number }> + _connected_future: Function | undefined + _readq: AsyncQueue; + _transport: SerialPort; + _nextIterator: { next: Function }; + _dataFrameReceived: { next: Function }; + logger: any; + + constructor(private writeCb: (data: Buffer) => Promise, logger: any) { + this.logger = logger; + this._pending = [(- 1), null]; + this._sendq = new AsyncQueue<{ data: Buffer, seq: number }>((iter: { next: Function }) => { + this._nextIterator = iter; + }); + this._readq = new AsyncQueue((iter: { next: Function }) => { + this._dataFrameReceived = iter; + }); + this._send_task(); + } + + data_received(data: Buffer) { + //console.log('data_received', data.toString('hex')) + /* Callback when there is data received from the uart */ + var frame; + if (data.indexOf(CANCEL) >= 0) { + this._buffer = new Buffer([]); + data = data.slice((data.lastIndexOf(CANCEL) + 1)); + } + if (data.indexOf(SUBSTITUTE) >= 0) { + this._buffer = new Buffer([]); + data = data.slice((data.indexOf(FLAG) + 1)); + } + if (this._buffer) { + this._buffer = Buffer.concat([this._buffer, data]); + } else { + this._buffer = data; + } + while (this._buffer) { + let retBuffer; + [frame, retBuffer] = this._extract_frame(this._buffer); + this._buffer = retBuffer as any; + if ((frame === null)) { + break; + } + this.frame_received(frame); + } + } + + _extract_frame(data: Buffer) { + /* Extract a frame from the data buffer */ + var place; + if (data.indexOf(FLAG) >= 0) { + place = data.indexOf(FLAG); + return [this._unstuff(data.slice(0, (place + 1))), data.slice((place + 1))]; + } + return [null, data]; + } + + frame_received(data: Buffer) { + /* Frame receive handler */ + if (((data[0] & 128) === 0)) { + this.data_frame_received(data); + } else { + if (((data[0] & 224) === 128)) { + this.ack_frame_received(data); + } else { + if (((data[0] & 224) === 160)) { + this.nak_frame_received(data); + } else { + if ((data[0] === 192)) { + this.rst_frame_received(data); + } else { + if ((data[0] === 193)) { + this.rstack_frame_received(data); + } else { + if ((data[0] === 194)) { + this.error_frame_received(data); + } else { + this.logger("UNKNOWN FRAME RECEIVED: %r", data); + } + } + } + } + } + } + } + + data_frame_received(data: Buffer) { + /* Data frame receive handler */ + var seq; + this.logger("Data frame: %s", data.toString('hex')); + seq = ((data[0] & 112) >> 4); + this._rec_seq = ((seq + 1) % 8); + this.write(this._ack_frame()); + this._handle_ack(data[0]); + if (this._dataFrameReceived) { + this._dataFrameReceived.next(this._randomize(data.slice(1, (- 3)))) + } + } + + [Symbol.asyncIterator]() { + return this._readq; + } + + ack_frame_received(data: Buffer) { + /* Acknowledgement frame receive handler */ + this.logger("ACK frame: %s", data.toString('hex')); + this._handle_ack(data[0]); + } + + nak_frame_received(data: Buffer) { + /* Negative acknowledgement frame receive handler */ + this.logger("NAK frame: %s", data.toString('hex')); + this._handle_nak(data[0]); + } + + rst_frame_received(data: Buffer) { + /* Reset frame handler */ + this.logger("RST frame: %s", data.toString('hex')); + } + + rstack_frame_received(data: Buffer) { + /* Reset acknowledgement frame receive handler */ + var code; + this._send_seq = 0; + this._rec_seq = 0; + try { + code = NcpResetCode[data[2]]; + } catch (e) { + code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; + } + this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { + return; + } + if ((!this._reset_deferred)) { + this.logger("Reset future is None"); + return; + } + this._reset_deferred.resolve(true); + } + + error_frame_received(data: Buffer) { + /* Error frame receive handler */ + this.logger("Error frame:", data.toString('hex')); + } + + write(data: Buffer) { + /* Send data to the uart */ + this.logger("Sending:", data.toString('hex')); + return this.writeCb(data); + } + + reset() { + /* Sends a reset frame */ + if ((this._reset_deferred)) { + throw new TypeError("reset can only be called on a new connection"); + } + this.write(this._rst_frame()); + this._reset_deferred = new Deferred(); + return this._reset_deferred.promise; + } + + private sleep(ms: number) { + return new Promise(resolve => { + setTimeout(resolve, ms) + }) + } + + async _send_task() { + for await (const item of this._sendq) { + let data, rxmit, seq, success; + //console.log('IteratorResult', item); + if ((item instanceof Terminator)) { + return; + } + if (!item) { + await this.sleep(5000); + continue; + } + data = (item as any).data; + seq = (item as any).seq; + success = false; + rxmit = 0; + while ((!success)) { + this._pending = { seq }; + try { + await this.write(this._data_frame(data, seq, rxmit)); + success = true; + } catch (e) { + rxmit = 1; + success = false; + } + } + } + } + + _handle_ack(control: number) { + /* Handle an acknowledgement frame */ + var ack, pending; + ack = (((control & 7) - 1) % 8); + if ((ack === this._pending[0])) { + [pending, this._pending] = [this._pending, [(- 1), null]]; + pending[1].set_result(true); + } + } + + _handle_nak(control: number) { + /* Handle negative acknowledgment frame */ + let nak = (control & 7); + if ((nak === this._pending[0])) { + this._pending[1].set_result(false); + } + } + + data(data: Buffer) { + /* Send a data frame */ + let seq = this._send_seq; + this._send_seq = ((seq + 1) % 8); + this._nextIterator.next({ data, seq }); + } + + _data_frame(data: Buffer, seq: number, rxmit: number) { + /* Construct a data frame */ + let control; + console.assert(((0 <= seq) && (seq <= 7))); + console.assert(((0 <= rxmit) && (rxmit <= 1))); + control = (((seq << 4) | (rxmit << 3)) | this._rec_seq); + return this._frame([control], this._randomize(data)); + } + + _ack_frame() { + /* Construct a acknowledgement frame */ + let control; + console.assert(((0 <= this._rec_seq) && (this._rec_seq < 8))); + + control = [(0b10000000 | (this._rec_seq & 0b00000111))]; + return this._frame(control); + } + + _rst_frame() { + /* Construct a reset frame */ + return Buffer.concat([Buffer.from([CANCEL]), this._frame([0xC0])]); + } + + _frame(control: ArrayLike, data?: ArrayLike) { + /* Construct a frame */ + const ctrlArr: Array = Array.from(control); + const dataArr: Array = (data && Array.from(data)) || []; + + const sum = ctrlArr.concat(dataArr); + + let crc = crc16ccitt(Buffer.from(sum), 65535); + let crcArr = [(crc >> 8), (crc % 256)]; + return Buffer.concat([this._stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); + } + + _randomize(s: Buffer) { + /*XOR s with a pseudo-random sequence for transmission + Used only in data frames + */ + let rand = 66; + let out = new Buffer(s.length); + let outIdx = 0; + for (let c of s){ + out.writeUInt8(c ^ rand, outIdx++); + if ((rand % 2)) { + rand = ((rand >> 1) ^ 0xB8); + } else { + rand = (rand >> 1); + } + } + return out; + } + + _stuff(s: Iterable): Buffer { + /* Byte stuff (escape) a string for transmission */ + let out = Buffer.alloc(256); + let outIdx = 0; + for (const c of s) { + if (RESERVED.includes(c)) { + out.writeUInt8(ESCAPE, outIdx++); + out.writeUInt8(c ^ 0x20, outIdx++); + } else { + out.writeUInt8(c, outIdx++); + } + } + return out.slice(0, outIdx); + } + + _unstuff(s: Buffer) { + /* Unstuff (unescape) a string after receipt */ + let escaped = false; + let out = new Buffer(s.length); + let outIdx = 0; + for (let idx = 0; idx < s.length; idx += 1) { + const c = s[idx]; + if (escaped) { + out.writeUInt8(c ^ 0x20, outIdx++); + escaped = false; + } else { + if ((c === 0x7D)) { + escaped = true; + } else { + out.writeUInt8(c, outIdx++); + } + } + } + return out; + } + + static connect(portAddress: string, connectionOptions: {}, logger: any): Promise<[UartProtocol, SerialPort]> { + const SerialPort = require('serialport'); + const port = new SerialPort(portAddress, connectionOptions); + const protocol = new UartProtocol((data: Buffer) => { + //console.log('Writing to port', portAddress, data.toString('hex')); + return new Promise((resolve, reject) => { + port.write(data, (err: Error) => { + if (!err) { + resolve(); + } else { + reject(err) + } + }); + }) + }, + logger + ); + + port.on('data', (data: any) => protocol.data_received(data)) + + return new Promise((resolve, reject) => { + port.on('open', () => { + logger('port open. resetting'); + protocol.reset().then( + () => { + logger('successfully reset'); + resolve([protocol, port]); + }, (err) => { + logger(err); + reject() + } + ); + }, (err: any) => { + logger(err); + reject() + }); + }) + } +} diff --git a/src/adapter/ezsp/driver/utils/crc16ccitt.ts b/src/adapter/ezsp/driver/utils/crc16ccitt.ts new file mode 100644 index 0000000000..a8033214e8 --- /dev/null +++ b/src/adapter/ezsp/driver/utils/crc16ccitt.ts @@ -0,0 +1,71 @@ +import { Buffer } from 'buffer'; + +const createBuffer = + Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow + ? Buffer.from + : // support for Node < 5.10 + (val: any) => Buffer.from(val); + + +function defineCrc(model: string, calc: Function) { + const fn = (buf: any, previous: any) => calc(buf, previous) >>> 0; + fn.signed = calc; + fn.unsigned = fn; + fn.model = model; + + return fn; +} + +// Generated by `./pycrc.py --algorithm=table-driven --model=ccitt --generate=c` +// prettier-ignore +let TABLE: number[] = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +]; + +//if (typeof Int32Array !== 'undefined') TABLE = new Int32Array(TABLE); + +const crc16ccitt = defineCrc('ccitt', function(buf: any, previous: any) { + if (!Buffer.isBuffer(buf)) buf = createBuffer(buf); + + let crc = typeof previous !== 'undefined' ? ~~previous : 0xffff; + + for (let index = 0; index < buf.length; index++) { + const byte = buf[index]; + crc = (TABLE[((crc >> 8) ^ byte) & 0xff] ^ (crc << 8)) & 0xffff; + } + + return crc; +}); + +export default crc16ccitt; diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts new file mode 100644 index 0000000000..11689fbde4 --- /dev/null +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -0,0 +1,133 @@ +if (!Symbol.asyncIterator){ + (Symbol).asyncIterator = Symbol.for("Symbol.asyncIterator"); +} + +export class Deferred { + + public promise: Promise; + public _resolve: Function; + public _reject: Function; + _isResolved = false; + _isRejected = false; + + constructor() { + this.promise = new Promise((resolve, reject) => { + this._resolve = resolve + this._reject = reject + }) + } + + public resolve(value:T){ + this._isResolved = true; + this._resolve(value); + } + + public reject(value:T){ + this._isResolved = true; + this.reject(value); + } + + get isResolved(){ + return this._isResolved; + } + + get isRejected(){ + return this._isRejected; + } + + get isFullfilled(){ + return this._isResolved || this._isRejected; + } +} + +//FROM: https://github.com/tc39/proposal-async-iteration/issues/99 +export class AsyncQueue { + private queue: Array<{ type: string, value: any }> = []; + private waiting: Array>> = []; + + constructor(initializer: Function) { + initializer({ + next: (value: T) => { + if (this.waiting.length > 0) { + // If anyone is waiting we'll just send them the value + // immediately + const consumer = this.waiting.shift(); + if (consumer) { + consumer.resolve({ + done: false, + value + }) + } + } else { + return this.queue.push({ + type: 'next', + value + }) + } + }, + throw: (error: any) => { + if (this.waiting.length > 0) { + const consumer = this.waiting.shift() + return consumer && consumer.reject(error) + } else { + return this.queue.push({ + value: error, + type: 'error' + }) + } + }, + return: (value: T) => { + if (this.waiting.length > 0) { + const consumer = this.waiting.shift() + return consumer && consumer.resolve({ + done: true, + value + }) + } else { + return this.queue.push({ + value, + type: 'return' + }) + } + } + }) + } + + next(): Promise>{ + if (this.queue.length > 1) { + // If there are items available then simply put them + // into the queue + const item = this.queue.shift(); + if (!item){ + throw new Error('Working around TS strictNullCheck'); + } + if (item.type === 'return') { + return Promise.resolve({ + done: true, + value: item.value + }) + } else if (item.type === 'error') { + return Promise.reject(item.value) + } else { + return Promise.resolve({ + done: false, + value: item.value + }) + } + } else { + // If there's nothing available then simply + // give back a Promise immediately for when a value eventually + // comes in + const def = new Deferred>(); + this.waiting.push(def) + return def.promise; + } + } + + [Symbol.asyncIterator] = () => { + return this + } +} + +import crc16ccitt from './crc16ccitt'; +export {crc16ccitt}; \ No newline at end of file From 687fa00c2c38c99954344bdd8b085a051d96599a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sun, 10 Jan 2021 14:21:57 +0300 Subject: [PATCH 02/63] next --- src/adapter/ezsp/adapter/ezspAdapter.ts | 44 ++++++++++++++++++++++++- src/adapter/ezsp/driver/driver.ts | 3 +- src/adapter/ezsp/driver/uart.ts | 28 ++++++++-------- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index ba0bb55dac..d6c41ea255 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -35,13 +35,55 @@ class EZSPAdapter extends Adapter { super(networkOptions, serialPortOptions, backupPath, adapterOptions); this.port = serialPortOptions; this.driver = new Driver(); + this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); + this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); + this.driver.on('incomingMessage', this.processMessage.bind(this)); + } + + private async processMessage(frame: any) { + // todo + if (!frame.senderEui64) { + frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) + } + this.emit('event', frame); + } + + private handleDeviceJoin(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device join request received: %s %s', nwk, ieee); + // let devices = this.getDevices(); + // if (!devices.some(d => d.nodeId === nwk || d.eui64 === ieee.toString())) { + // devices.push({ nodeId: nwk, eui64: ieee.toString() }); + // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); + // log.info('Added device to DB: %s %s', nwk, ieee) + // } + } + + private handleDeviceLeft(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device left network request received: %s %s', nwk, ieee); + // let devices = this.getDevices(); + + // let idx = devices.findIndex(d => d.nodeId === nwk && d.eui64 === ieee.toString()); + // if (idx >= 0) { + // devices = devices.splice(idx, 1); + // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); + // } } /** * Adapter methods */ public async start(): Promise { - await this.driver.startup(this.port.path, {}, debug); + await this.driver.startup(this.port.path, { + baudRate: this.port.baudRate || 115200, + parity: 'none', + stopBits: 1, + xon: true, + xoff: true + }, debug).then(()=>this.driver.getNetworkParameters()); return "resumed"; } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 27e5f7a312..168fb9d353 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -242,7 +242,8 @@ export class Driver extends EventEmitter { public async getNetworkParameters() : Promise<{nodeType: number, networkParams: EmberNetworkParameters}> { let [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); // kirov 0x93 NOT_JOINED - if (status != 0 && status != 0x93) + //if (status != 0 && status != 0x93) + if (status != 0) throw new Error('Unable to obtain network parameters'); return {nodeType, networkParams} } diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index e22188478e..160722a3ad 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -7,7 +7,9 @@ const XON = 0x11 // Resume transmission const XOFF = 0x13 // Stop transmission const SUBSTITUTE = 0x18 const CANCEL = 0x1A // Terminates a frame in progress - +const STUFF = 0x20 +const RANDOMIZE_START = 0x42 +const RANDOMIZE_SEQ = 0xB8 const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] class Terminator { @@ -57,11 +59,11 @@ export class UartProtocol implements AsyncIterable { /* Callback when there is data received from the uart */ var frame; if (data.indexOf(CANCEL) >= 0) { - this._buffer = new Buffer([]); + this._buffer = Buffer.alloc(0); data = data.slice((data.lastIndexOf(CANCEL) + 1)); } if (data.indexOf(SUBSTITUTE) >= 0) { - this._buffer = new Buffer([]); + this._buffer = Buffer.alloc(0); data = data.slice((data.indexOf(FLAG) + 1)); } if (this._buffer) { @@ -82,9 +84,9 @@ export class UartProtocol implements AsyncIterable { _extract_frame(data: Buffer) { /* Extract a frame from the data buffer */ - var place; - if (data.indexOf(FLAG) >= 0) { - place = data.indexOf(FLAG); + const place = data.indexOf(FLAG); + if (place >= 0) { + // todo: check crc data return [this._unstuff(data.slice(0, (place + 1))), data.slice((place + 1))]; } return [null, data]; @@ -293,13 +295,13 @@ export class UartProtocol implements AsyncIterable { /*XOR s with a pseudo-random sequence for transmission Used only in data frames */ - let rand = 66; - let out = new Buffer(s.length); + let rand = RANDOMIZE_START; + let out = Buffer.alloc(s.length); let outIdx = 0; for (let c of s){ out.writeUInt8(c ^ rand, outIdx++); if ((rand % 2)) { - rand = ((rand >> 1) ^ 0xB8); + rand = ((rand >> 1) ^ RANDOMIZE_SEQ); } else { rand = (rand >> 1); } @@ -314,7 +316,7 @@ export class UartProtocol implements AsyncIterable { for (const c of s) { if (RESERVED.includes(c)) { out.writeUInt8(ESCAPE, outIdx++); - out.writeUInt8(c ^ 0x20, outIdx++); + out.writeUInt8(c ^ STUFF, outIdx++); } else { out.writeUInt8(c, outIdx++); } @@ -325,15 +327,15 @@ export class UartProtocol implements AsyncIterable { _unstuff(s: Buffer) { /* Unstuff (unescape) a string after receipt */ let escaped = false; - let out = new Buffer(s.length); + let out = Buffer.alloc(s.length); let outIdx = 0; for (let idx = 0; idx < s.length; idx += 1) { const c = s[idx]; if (escaped) { - out.writeUInt8(c ^ 0x20, outIdx++); + out.writeUInt8(c ^ STUFF, outIdx++); escaped = false; } else { - if ((c === 0x7D)) { + if ((c === ESCAPE)) { escaped = true; } else { out.writeUInt8(c, outIdx++); From 1b8af4048e249a7bf1ebf400439121f2664e9390 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 14 Jan 2021 23:06:56 +0300 Subject: [PATCH 03/63] start describe multicast --- src/adapter/ezsp/adapter/ezspAdapter.ts | 3 +- src/adapter/ezsp/driver/driver.ts | 11 +++++-- src/adapter/ezsp/driver/ezsp.ts | 18 ++++++++++- src/adapter/ezsp/driver/multicast.ts | 41 +++++++++++++++++++++++++ src/adapter/ezsp/driver/types/named.ts | 2 +- 5 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/adapter/ezsp/driver/multicast.ts diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index d6c41ea255..6698c8628d 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -83,7 +83,8 @@ class EZSPAdapter extends Adapter { stopBits: 1, xon: true, xoff: true - }, debug).then(()=>this.driver.getNetworkParameters()); + }, debug); + await this.driver.getNetworkParameters(); return "resumed"; } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 168fb9d353..d6af6218b8 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -4,13 +4,15 @@ import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters } from './types/struct'; import { Deferred } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberDeviceUpdate } from './types/named'; - +import { Multicast } from './multicast'; export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT private ezsp: Ezsp; private eui64ToNodeId = new Map(); private pending = new Map>>(); + private logger: any; + private _multicast: Multicast; constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ super(); @@ -24,7 +26,8 @@ export class Driver extends EventEmitter { } public async startup(port: string, options: {}, logger: any) { - let ezsp = this.ezsp = new Ezsp(logger); + this.logger = logger; + let ezsp = this.ezsp = new Ezsp(this.logger); await ezsp.connect(port, options); const version = await ezsp.version(); console.log('Got version', version); @@ -45,6 +48,10 @@ export class Driver extends EventEmitter { const state = await ezsp.execCommand('networkState'); console.log('Network state', state); } + const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); + this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + this._multicast = new Multicast(ezsp, logger); + await this._multicast.initialize(); } private handleFrame(frameName: string, ...args: any[]) { diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 0b5bec5235..1486827e65 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -6,6 +6,7 @@ import { Deferred } from './utils'; import { EmberStatus, EmberOutgoingMessageType } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; +import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; export class Ezsp extends EventEmitter { ezsp_version = 4; @@ -66,10 +67,25 @@ export class Ezsp extends EventEmitter { async setConfigurationValue(configId: number, value: any) { let ret; [ret] = await this.execCommand('setConfigurationValue', configId, value); - console.assert(ret === 0); + console.assert(ret === EmberStatus.SUCCESS); this.logger('Set %s = %s', configId, value); } + async getConfigurationValue(configId: number) { + let ret, value; + [ret, value] = await this.execCommand('getConfigurationValue', configId); + console.assert(ret === EmberStatus.SUCCESS); + this.logger('Get %s = %s', configId, value); + return [ret, value]; + } + + async getMulticastTableEntry(index: number) { + let ret, value; + [ret, value] = await this.execCommand('getMulticastTableEntry', index); + console.assert(ret === EmberStatus.SUCCESS); + return [ret, value]; + } + close() { return this._port.close(); } diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts new file mode 100644 index 0000000000..5382ffbc23 --- /dev/null +++ b/src/adapter/ezsp/driver/multicast.ts @@ -0,0 +1,41 @@ +import { Ezsp } from './ezsp'; +import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; +import { EmberStatus, EmberOutgoingMessageType } from './types/named'; +import { EmberMulticastTableEntry } from './types/struct'; + + +export class Multicast { + TABLE_SIZE = 16; + private _ezsp: Ezsp; + private logger: any; + private _multicast: any; + private _available: Set; + + constructor(ezsp: Ezsp, logger: any){ + this._ezsp = ezsp; + this.logger = logger; + this._multicast = {}; + this._available = new Set(); + } + + async initialize() { + const [status, size] = await this._ezsp.getConfigurationValue( + EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE + ); + if (status !== EmberStatus.SUCCESS) return; + for (let i = 0; i < size; i++) { + let st: any, entry: any; + [st, entry] = await this._ezsp.getMulticastTableEntry(i); + if (st !== EmberStatus.SUCCESS) { + this.logger("Couldn't get MulticastTableEntry #%s: %s", i, st); + continue; + } + this.logger("MulticastTableEntry[%s] = %s", i, entry); + if (entry.endpoint !== 0) { + this._multicast[entry.multicastId] = [entry, i]; + } else { + this._available.add(i); + } + } + } +} \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 680cbca68c..674bab4c99 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -26,7 +26,7 @@ export class EmberNodeId extends basic.uint16_t { } export class EmberPanId extends basic.uint16_t { } -export class EmberMulticastId extends basic.uint16_t { +export class EmberMulticastId extends basic.uint8_t { } export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { From f9a87e05797781b3c6310ef0e3e509c6fe7e057f Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sat, 16 Jan 2021 21:08:37 +0300 Subject: [PATCH 04/63] try to start network --- src/adapter/ezsp/adapter/ezspAdapter.ts | 4 +- src/adapter/ezsp/driver/driver.ts | 68 +++++++++++++++++-------- src/adapter/ezsp/driver/ezsp.ts | 16 ++++++ src/adapter/ezsp/driver/multicast.ts | 59 +++++++++++++++++++-- src/adapter/ezsp/driver/types/basic.ts | 10 +++- src/adapter/ezsp/driver/types/struct.ts | 21 ++++---- 6 files changed, 140 insertions(+), 38 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 6698c8628d..f8a649e125 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -83,8 +83,8 @@ class EZSPAdapter extends Adapter { stopBits: 1, xon: true, xoff: true - }, debug); - await this.driver.getNetworkParameters(); + }, this.networkOptions, debug); + // await this.driver.getNetworkParameters(); return "resumed"; } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index d6af6218b8..3335bd99ce 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,14 +1,16 @@ +import * as TsType from './../../tstype'; import { Ezsp } from './ezsp'; import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters } from './types/struct'; import { Deferred } from './utils'; -import { EmberOutgoingMessageType, EmberEUI64, EmberDeviceUpdate } from './types/named'; +import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId } from './types/named'; import { Multicast } from './multicast'; export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT - private ezsp: Ezsp; + private _ezsp: Ezsp; + private _nwkOpt: TsType.NetworkOptions; private eui64ToNodeId = new Map(); private pending = new Map>>(); private logger: any; @@ -25,10 +27,11 @@ export class Driver extends EventEmitter { } } - public async startup(port: string, options: {}, logger: any) { + public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions, logger: any) { this.logger = logger; - let ezsp = this.ezsp = new Ezsp(this.logger); - await ezsp.connect(port, options); + this._nwkOpt = nwkOpt; + let ezsp = this._ezsp = new Ezsp(this.logger); + await ezsp.connect(port, serialOpt); const version = await ezsp.version(); console.log('Got version', version); @@ -45,13 +48,38 @@ export class Driver extends EventEmitter { console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) } else { + await this.form_network(); const state = await ezsp.execCommand('networkState'); console.log('Network state', state); } const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); this._multicast = new Multicast(ezsp, logger); - await this._multicast.initialize(); + await this._multicast.startup([]); + } + + private async form_network() { + const panID = this._nwkOpt.panID; + const extendedPanID = this._nwkOpt.extendedPanID; + const hashed_tclk = this._ezsp.ezsp_version > 4; + // const initial_security_state = bellows.zigbee.util.zha_security( + // nwk, controller=True, hashed_tclk=hashed_tclk + // ) + // const [status] = await this._ezsp.setInitialSecurityState(initial_security_state); + const parameters:EmberNetworkParameters = new EmberNetworkParameters(); + parameters.panId = panID; + parameters.extendedPanId = extendedPanID; + parameters.radioTxPower = 8; + parameters.radioChannel = 15; + parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; + parameters.nwkManagerId = 0; + parameters.nwkUpdateId = 0; + parameters.channels = 0x07FFF800; + + await this._ezsp.formNetwork(parameters); + await this._ezsp.setValue( + EzspValueId.VALUE_STACK_TOKEN_WRITING, 1 + ); } private handleFrame(frameName: string, ...args: any[]) { @@ -150,7 +178,7 @@ export class Driver extends EventEmitter { let strEui64 = eui64.toString(); let nodeId = this.eui64ToNodeId.get(strEui64); if (nodeId === undefined) { - nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64).then(arr => arr[0]); + nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64).then(arr => arr[0]); if (nodeId && nodeId !== 0xFFFF) { this.eui64ToNodeId.set(strEui64, nodeId); } else { @@ -160,7 +188,7 @@ export class Driver extends EventEmitter { nwk = nodeId; } - let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); console.log('unicast message sent, waiting for reply'); if (v[0] != 0) { this.pending.delete(seq); @@ -220,11 +248,11 @@ export class Driver extends EventEmitter { } public stop() { - return this.ezsp.close(); + return this._ezsp.close(); } public getLocalEUI64(): Promise { - return this.ezsp.execCommand('getEui64') + return this._ezsp.execCommand('getEui64') .then(ret => new EmberEUI64(ret[0] as any)); } @@ -232,7 +260,7 @@ export class Driver extends EventEmitter { for (let [eUI64, value] of this.eui64ToNodeId) { if (value === nwk) return new EmberEUI64(eUI64); } - let value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); + let value = await this._ezsp.execCommand('lookupEui64ByNodeId', nwk); if (value[0] === 0) { let eUI64 = new EmberEUI64(value[1] as any); this.eui64ToNodeId.set(eUI64.toString(), nwk); @@ -243,15 +271,15 @@ export class Driver extends EventEmitter { } public permitJoining(seconds:number){ - return this.ezsp.execCommand('permitJoining', seconds); + return this._ezsp.execCommand('permitJoining', seconds); } - public async getNetworkParameters() : Promise<{nodeType: number, networkParams: EmberNetworkParameters}> { - let [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); - // kirov 0x93 NOT_JOINED - //if (status != 0 && status != 0x93) - if (status != 0) - throw new Error('Unable to obtain network parameters'); - return {nodeType, networkParams} - } + // public async getNetworkParameters() : Promise<{nodeType: number, networkParams: EmberNetworkParameters}> { + // let [status, nodeType, networkParams] = await this._ezsp.execCommand('getNetworkParameters'); + // // kirov 0x93 NOT_JOINED + // //if (status != 0 && status != 0x93) + // if (status != 0) + // throw new Error('Unable to obtain network parameters'); + // return {nodeType, networkParams}; + // } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 1486827e65..bedbd99caa 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -85,6 +85,20 @@ export class Ezsp extends EventEmitter { console.assert(ret === EmberStatus.SUCCESS); return [ret, value]; } + + async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry) { + let ret; + [ret] = await this.execCommand('setMulticastTableEntry', index, entry); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + + async setValue(valueId: t.EzspValueId, value: any) { + let ret; + [ret] = await this.execCommand('setValue', valueId, value); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } close() { return this._port.close(); @@ -163,10 +177,12 @@ export class Ezsp extends EventEmitter { }) v = await this._command("formNetwork", parameters); if ((v[0] !== 0)) { + this.logger("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } v = await fut.promise; if ((v[0] !== 0x90 /*EmberStatus.NETWORK_UP*/)) { + this.logger("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } return v; diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 5382ffbc23..ed8131f616 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -1,6 +1,7 @@ import { Ezsp } from './ezsp'; import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; -import { EmberStatus, EmberOutgoingMessageType } from './types/named'; +import * as t from './types/basic'; +import { EmberStatus, EmberOutgoingMessageType, EmberMulticastId } from './types/named'; import { EmberMulticastTableEntry } from './types/struct'; @@ -9,16 +10,16 @@ export class Multicast { private _ezsp: Ezsp; private logger: any; private _multicast: any; - private _available: Set; + private _available: Array; constructor(ezsp: Ezsp, logger: any){ this._ezsp = ezsp; this.logger = logger; this._multicast = {}; - this._available = new Set(); + this._available = []; } - async initialize() { + private async _initialize() { const [status, size] = await this._ezsp.getConfigurationValue( EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE ); @@ -34,8 +35,56 @@ export class Multicast { if (entry.endpoint !== 0) { this._multicast[entry.multicastId] = [entry, i]; } else { - this._available.add(i); + this._available.push(i); } } } + + async startup(enpoints: Array) { + await this._initialize(); + for (let ep of enpoints) { + if (!ep.id) continue; + for (let group_id of ep.member_of) { + await this.subscribe(group_id); + } + } + } + + async subscribe(group_id: number):Promise { + if (this._multicast.indexOf(group_id) >= 0) { + this.logger("%s is already subscribed", group_id); + return EmberStatus.SUCCESS; + } + + try { + const idx = this._available.pop(); + const entry:EmberMulticastTableEntry = new EmberMulticastTableEntry(); + entry.endpoint = 1; + entry.multicastId = group_id; + entry.networkIndex = 0; + const [status] = await this._ezsp.setMulticastTableEntry(idx, entry); + if (status !== EmberStatus.SUCCESS) { + this.logger( + "Set MulticastTableEntry #%s for %s multicast id: %s", + idx, + entry.multicastId, + status, + ) + this._available.push(idx); + return status; + } + + this._multicast[entry.multicastId] = [entry, idx]; + this.logger( + "Set MulticastTableEntry #%s for %s multicast id: %s", + idx, + entry.multicastId, + status, + ) + return status; + } catch (e) { + this.logger("No more available slots MulticastId subscription"); + return EmberStatus.INDEX_OUT_OF_RANGE; + } + } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index ccf721e065..ef38698872 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -98,7 +98,7 @@ export class LVBytes { export abstract class List { static serialize(cls: any, value: any[]) { console.assert(((cls._length === null) || (cls.length === cls._length))); - return value.map(i => i.serialize(cls, i)); + return Buffer.from(value.map(i => i.serialize(cls, i))); } static deserialize(cls: any, data: Buffer): (any[] | Buffer)[] { var item; @@ -116,7 +116,7 @@ class _LVList extends List { var data, head; head = [cls.length]; data = super.serialize(cls, value); - return head.concat(data); + return Buffer.from(head.concat(data)); } static deserialize(cls: any, data: Buffer) { var item, length; @@ -144,6 +144,12 @@ export function LVList(itemtype: any) : List { } class _FixedList extends List { + static serialize(cls: any, value: any[]) { + var data, head; + head = [cls._length]; + data = value.map(i => cls._itemtype.serialize(cls._itemtype, i)[0]); + return Buffer.from(head.concat(data)); + } static deserialize(cls: any, data: Buffer) { let item; let r: any[] = []; diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 1e5df8b586..25da0493b2 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -25,18 +25,18 @@ export class EzspStruct { } return [r, data] } - - /*toString() { - //r = '<%s ' % (self.__class__.__name__, ) - //r += ' '.join( - // ['%s=%s' % (f[0], getattr(self, f[0], None)) for f in self._fields] - //) - //r += '>' - //return r - }*/ } export class EmberNetworkParameters extends EzspStruct { + public extendedPanId: number[]; + public panId: number; + public radioTxPower: number; + public radioChannel: number; + public joinMethod: named.EmberJoinMethod; + public nwkManagerId: named.EmberNodeId; + public nwkUpdateId: number; + public channels: number; + static _fields = [ // The network's extended PAN identifier. ['extendedPanId', basic.fixed_list(8, basic.uint8_t)], @@ -137,6 +137,9 @@ export class EmberBindingTableEntry extends EzspStruct { } export class EmberMulticastTableEntry extends EzspStruct { + public multicastId: number; + public endpoint: number; + public networkIndex: number; // 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. From 5fed6ecfe3b7ae6011e2b12e69bb5a99e3900a56 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 17 Jan 2021 16:45:35 +0300 Subject: [PATCH 05/63] form network --- src/adapter/ezsp/driver/driver.ts | 26 ++++++----- src/adapter/ezsp/driver/ezsp.ts | 57 +++++++++---------------- src/adapter/ezsp/driver/types/basic.ts | 6 +-- src/adapter/ezsp/driver/types/named.ts | 11 ++++- src/adapter/ezsp/driver/types/struct.ts | 22 ++++++---- src/adapter/ezsp/driver/utils/index.ts | 31 +++++++++++++- 6 files changed, 90 insertions(+), 63 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 3335bd99ce..dde6189ef0 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -2,8 +2,8 @@ import * as TsType from './../../tstype'; import { Ezsp } from './ezsp'; import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; import { EventEmitter } from "events"; -import { EmberApsFrame, EmberNetworkParameters } from './types/struct'; -import { Deferred } from './utils'; +import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; +import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId } from './types/named'; import { Multicast } from './multicast'; @@ -35,7 +35,12 @@ export class Driver extends EventEmitter { const version = await ezsp.version(); console.log('Got version', version); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SECURITY_LEVEL, 5); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS, @@ -43,6 +48,9 @@ export class Driver extends EventEmitter { | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 8); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); if (await ezsp.networkInit()) { console.log('Network ready'); @@ -62,10 +70,8 @@ export class Driver extends EventEmitter { const panID = this._nwkOpt.panID; const extendedPanID = this._nwkOpt.extendedPanID; const hashed_tclk = this._ezsp.ezsp_version > 4; - // const initial_security_state = bellows.zigbee.util.zha_security( - // nwk, controller=True, hashed_tclk=hashed_tclk - // ) - // const [status] = await this._ezsp.setInitialSecurityState(initial_security_state); + const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt, true, hashed_tclk); + const [status] = await this._ezsp.setInitialSecurityState(initial_security_state); const parameters:EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; parameters.extendedPanId = extendedPanID; @@ -74,12 +80,10 @@ export class Driver extends EventEmitter { parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; parameters.nwkManagerId = 0; parameters.nwkUpdateId = 0; - parameters.channels = 0x07FFF800; - + parameters.channels = 34635776; //0x07FFF800; + await this._ezsp.formNetwork(parameters); - await this._ezsp.setValue( - EzspValueId.VALUE_STACK_TOKEN_WRITING, 1 - ); + await this._ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); } private handleFrame(frameName: string, ...args: any[]) { diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index bedbd99caa..a294b3d1e9 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -93,6 +93,20 @@ export class Ezsp extends EventEmitter { return [ret]; } + async setInitialSecurityState(entry: t.EmberInitialSecurityState) { + let ret; + [ret] = await this.execCommand('setInitialSecurityState', entry); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + + async getCurrentSecurityState(){ + let ret, res; + [ret, res] = await this.execCommand('getCurrentSecurityState'); + console.assert(ret === EmberStatus.SUCCESS); + return [ret, res]; + } + async setValue(valueId: t.EzspValueId, value: any) { let ret; [ret] = await this.execCommand('setValue', valueId, value); @@ -109,7 +123,6 @@ export class Ezsp extends EventEmitter { c = (COMMANDS)[name]; data = t.serialize(args, c[1]); frame = [(this._seq & 255)]; - // kirov if ((this.ezsp_version < 8)) { if ((this.ezsp_version >= 5)) { frame.push(0x00, 0xFF, 0x00, c[0]); @@ -125,8 +138,9 @@ export class Ezsp extends EventEmitter { private _command(name: string, ...args: any[]): Promise { var c, data, deferred; - this.logger("Send command %s", name); + this.logger(`Send command ${name}: (${args})`); data = this._ezsp_frame(name, ...args); + this.logger(`Send data ${name}: (${data.toString('hex')})`); this._gw.data(data); c = (COMMANDS)[name]; deferred = new Deferred(); @@ -135,40 +149,8 @@ export class Ezsp extends EventEmitter { return deferred.promise; } - /* private async _list_command(name: string, item_frames: Array, completion_frame: any, spos: number, ...args: any[]) { - // Run a command, returning result callbacks as a list - var cbid, fut: Deferred, v; - var cb; - fut = new Deferred(); - let results: any[] = []; - cb = (frameName: string, response: any) => { - if (item_frames.indexOf(frameName) >= 0) { - results.push(response); - } else { - if ((frameName === completion_frame)) { - fut.resolve(response); - } - } - }; - cbid = this.add_callback(cb); - try { - v = await this._command(name, ...args); - if ((v[0] !== 0)) { - throw new Error(v); - } - v = await fut.promise; - if ((v[spos] !== 0)) { - throw new Error(v); - } - } finally { - this.remove_callback(cbid); - } - return results; - }*/ - - async formNetwork(parameters: {}) { - var fut: Deferred, v; + var fut: Deferred, v, st; fut = new Deferred(); this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { @@ -181,7 +163,7 @@ export class Ezsp extends EventEmitter { throw new Error(("Failure forming network:" + v)); } v = await fut.promise; - if ((v[0] !== 0x90 /*EmberStatus.NETWORK_UP*/)) { + if ((v !== 0x90 /*EmberStatus.NETWORK_UP*/)) { this.logger("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } @@ -203,7 +185,6 @@ export class Ezsp extends EventEmitter { data randomization removed. */ var frame_id: number, result, schema, sequence; - // kirov if ((this.ezsp_version < 8)) { [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; } else { @@ -227,12 +208,14 @@ export class Ezsp extends EventEmitter { if (entry) { console.assert(entry.expectedId === frame_id); [result, data] = t.deserialize(data, entry.schema); + this.logger(`Application frame ${frame_id} (${frameName}): ${result}`); entry.deferred.resolve(result); } } else { schema = cmd.outArgs; frameName = cmd.name; [result, data] = t.deserialize(data, schema); + this.logger(`Application frame ${frame_id} (${frameName}): ${result}`); super.emit('frame', frameName, ...result); } if ((frame_id === 0)) { diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index ef38698872..778a4411a5 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -145,10 +145,8 @@ export function LVList(itemtype: any) : List { class _FixedList extends List { static serialize(cls: any, value: any[]) { - var data, head; - head = [cls._length]; - data = value.map(i => cls._itemtype.serialize(cls._itemtype, i)[0]); - return Buffer.from(head.concat(data)); + const data = value.map(i => cls._itemtype.serialize(cls._itemtype, i)[0]); + return Buffer.from(data); } static deserialize(cls: any, data: Buffer) { let item; diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 674bab4c99..f8b2984272 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -61,7 +61,8 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { value = (value as EmberEUI64).value as number[]; } console.assert(cls._length === value.length); - return (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)); + const val = (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)); + return Buffer.from(val as any[]); } public get value() { @@ -253,6 +254,14 @@ export class EzspConfigId extends basic.uint8_t { // again on the trust center. The default value is 300 seconds, i.e., 5 // minutes. static CONFIG_TRANSIENT_KEY_TIMEOUT_S = 0x36 + // The number of passive acknowledgements to record from neighbors before we stop + // re-transmitting broadcasts + static CONFIG_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 emAllowTcRejoinsUsingWellKnownKeyTimeoutSec value. + static CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38 } diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 25da0493b2..17c9fdd2de 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -2,12 +2,6 @@ import * as basic from './basic'; import * as named from './named'; export class EzspStruct { - constructor(...args: any[]) { - // if len(args) == 1 and isinstance(args[0], self.__class__): - // # copy constructor - // for field in self._fields: - // setattr(self, field[0], getattr(args[0], field[0])) - } static serialize(cls: any, obj: any) { return Buffer.concat(cls._fields.map( (field:any[]) => { let value = obj[field[0]]; @@ -25,6 +19,10 @@ export class EzspStruct { } return [r, data] } + + public toString():string { + return `${this.constructor.name}: ${JSON.stringify(this)}`; + } } export class EmberNetworkParameters extends EzspStruct { @@ -83,7 +81,6 @@ export class EmberZigbeeNetwork extends EzspStruct { } export class EmberApsFrame extends EzspStruct { - public profileId: number; public sequence: number; public clusterId: number; @@ -155,6 +152,7 @@ export class EmberMulticastTableEntry extends EzspStruct { } export class EmberKeyData extends EzspStruct { + public contents: Buffer; // A 128- bit key. static _fields = [ // The key data. @@ -163,7 +161,7 @@ export class EmberKeyData extends EzspStruct { } export class EmberCertificateData extends EzspStruct { - + public contents: Buffer; // The implicit certificate used in CBKE. static _fields = [ // The certificate data. @@ -173,6 +171,7 @@ export class EmberCertificateData extends EzspStruct { } export class EmberPublicKeyData extends EzspStruct { + public contents: Buffer; // The public key data used in CBKE. static _fields = [ // The public key data. @@ -181,6 +180,7 @@ export class EmberPublicKeyData extends EzspStruct { } export class EmberPrivateKeyData extends EzspStruct { + public contents: Buffer; // The private key data used in CBKE. static _fields = [ // The private key data. @@ -306,6 +306,12 @@ export class EmberRouteTableEntry extends EzspStruct { } export class EmberInitialSecurityState extends EzspStruct { + public bitmask: number; + public preconfiguredKey: EmberKeyData; + public networkKey: EmberKeyData; + public networkKeySequenceNumber: number; + public preconfiguredTrustCenterEui64: named.EmberEUI64; + // The security data used to set the configuration for the stack, or the // retrieved configuration currently in use. static _fields = [ diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index 11689fbde4..d85d3bc75c 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -1,3 +1,7 @@ +import crc16ccitt from './crc16ccitt'; +import { EmberInitialSecurityState, EmberKeyData } from '../types/struct'; +import { EmberInitialSecurityBitmask, EmberEUI64 } from '../types/named'; + if (!Symbol.asyncIterator){ (Symbol).asyncIterator = Symbol.for("Symbol.asyncIterator"); } @@ -129,5 +133,28 @@ export class AsyncQueue { } } -import crc16ccitt from './crc16ccitt'; -export {crc16ccitt}; \ No newline at end of file + +function ember_security(config: any, controller: boolean = false, hashed_tclk: boolean = true):EmberInitialSecurityState { + const isc: EmberInitialSecurityState = new EmberInitialSecurityState(); + isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | EmberInitialSecurityBitmask.REQUIRE_ENCRYPTED_KEY); + isc.preconfiguredKey = new EmberKeyData(); + isc.preconfiguredKey.contents = Buffer.from("ZigBeeAlliance09"); + isc.networkKey = new EmberKeyData(); + isc.networkKey.contents = config.networkKey; + isc.networkKeySequenceNumber = 0; + isc.preconfiguredTrustCenterEui64 = new EmberEUI64([0, 0, 0, 0, 0, 0, 0, 0]); + + if (controller) { + isc.bitmask |= ( + EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | EmberInitialSecurityBitmask.HAVE_NETWORK_KEY + ) + if (hashed_tclk) { + isc.preconfiguredKey = new EmberKeyData(); + isc.preconfiguredKey.contents = Buffer.from("ZigBeeAlliance09"); + isc.bitmask |= EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY; + } + } + return isc; +} + +export {crc16ccitt, ember_security}; \ No newline at end of file From 156ae4ebb9a713ef792e8a262476091f93be7c22 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 17 Jan 2021 22:02:20 +0300 Subject: [PATCH 06/63] started --- src/adapter/ezsp/adapter/ezspAdapter.ts | 10 +++-- src/adapter/ezsp/driver/driver.ts | 57 +++++++++++++++---------- src/adapter/ezsp/driver/ezsp.ts | 38 +++++++++++++++-- 3 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index f8a649e125..318a720634 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -84,8 +84,7 @@ class EZSPAdapter extends Adapter { xon: true, xoff: true }, this.networkOptions, debug); - // await this.driver.getNetworkParameters(); - return "resumed"; + return Promise.resolve("resumed"); } public async stop(): Promise { @@ -208,8 +207,11 @@ class EZSPAdapter extends Adapter { } public async getNetworkParameters(): Promise { - // todo - return Promise.reject(); + return { + panID: this.driver.networkParams.panId, + extendedPanID: this.driver.networkParams.extendedPanId[0], + channel: this.driver.networkParams.radioChannel + }; } public async supportsBackup(): Promise { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index dde6189ef0..7679e9ef9f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,6 +1,6 @@ import * as TsType from './../../tstype'; import { Ezsp } from './ezsp'; -import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; +import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, EmberNodeId } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; import { Deferred, ember_security } from './utils'; @@ -11,9 +11,12 @@ export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT private _ezsp: Ezsp; private _nwkOpt: TsType.NetworkOptions; + public networkParams: EmberNetworkParameters; private eui64ToNodeId = new Map(); private pending = new Map>>(); private logger: any; + private _nwk: EmberNodeId; + private _ieee: EmberEUI64; private _multicast: Multicast; constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ @@ -52,16 +55,36 @@ export class Driver extends EventEmitter { await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); - if (await ezsp.networkInit()) { - console.log('Network ready'); - ezsp.on('frame', this.handleFrame.bind(this)) - } else { + if (!await ezsp.networkInit()) { await this.form_network(); const state = await ezsp.execCommand('networkState'); console.log('Network state', state); } - const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); - this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); + console.assert(status == EmberStatus.SUCCESS); + if (nodeType != EmberNodeType.COORDINATOR) { + this.logger(`Leaving current network as ${nodeType} and forming new network`); + const [st] = await this._ezsp.leaveNetwork(); + console.assert(st == EmberStatus.NETWORK_DOWN); + await this.form_network(); + [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); + console.assert(status == EmberStatus.SUCCESS); + } + this.networkParams = networkParams; + this.logger("Node type: %s, Network parameters: %s", nodeType, networkParams); + + await ezsp.updatePolicies({}); + + const [nwk] = await ezsp.execCommand('getNodeId'); + this._nwk = nwk; + this._ieee = await this.getLocalEUI64(); + // this.handle_join(this.nwk, this.ieee, 0); + console.log('Network ready'); + ezsp.on('frame', this.handleFrame.bind(this)) + this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); + + // const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); + // this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); this._multicast = new Multicast(ezsp, logger); await this._multicast.startup([]); } @@ -76,11 +99,11 @@ export class Driver extends EventEmitter { parameters.panId = panID; parameters.extendedPanId = extendedPanID; parameters.radioTxPower = 8; - parameters.radioChannel = 15; + parameters.radioChannel = this._nwkOpt.channelList[0]; parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; parameters.nwkManagerId = 0; parameters.nwkUpdateId = 0; - parameters.channels = 34635776; //0x07FFF800; + parameters.channels = 0x07FFF800; // all channels await this._ezsp.formNetwork(parameters); await this._ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); @@ -182,7 +205,7 @@ export class Driver extends EventEmitter { let strEui64 = eui64.toString(); let nodeId = this.eui64ToNodeId.get(strEui64); if (nodeId === undefined) { - nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64).then(arr => arr[0]); + nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64); if (nodeId && nodeId !== 0xFFFF) { this.eui64ToNodeId.set(strEui64, nodeId); } else { @@ -256,8 +279,7 @@ export class Driver extends EventEmitter { } public getLocalEUI64(): Promise { - return this._ezsp.execCommand('getEui64') - .then(ret => new EmberEUI64(ret[0] as any)); + return this._ezsp.execCommand('getEui64'); } public async networkIdToEUI64(nwk: number): Promise { @@ -265,7 +287,7 @@ export class Driver extends EventEmitter { if (value === nwk) return new EmberEUI64(eUI64); } let value = await this._ezsp.execCommand('lookupEui64ByNodeId', nwk); - if (value[0] === 0) { + if (value[0] === EmberStatus.SUCCESS) { let eUI64 = new EmberEUI64(value[1] as any); this.eui64ToNodeId.set(eUI64.toString(), nwk); return eUI64; @@ -277,13 +299,4 @@ export class Driver extends EventEmitter { public permitJoining(seconds:number){ return this._ezsp.execCommand('permitJoining', seconds); } - - // public async getNetworkParameters() : Promise<{nodeType: number, networkParams: EmberNetworkParameters}> { - // let [status, nodeType, networkParams] = await this._ezsp.execCommand('getNetworkParameters'); - // // kirov 0x93 NOT_JOINED - // //if (status != 0 && status != 0x93) - // if (status != 0) - // throw new Error('Unable to obtain network parameters'); - // return {nodeType, networkParams}; - // } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index a294b3d1e9..a82205bd46 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -13,6 +13,7 @@ export class Ezsp extends EventEmitter { _gw: UartProtocol; _port : any; _seq = 0; + _tc_policy: any; _awaiting = new Map }>(); COMMANDS_BY_ID = new Map(); _cbCounter = 0; @@ -64,6 +65,27 @@ export class Ezsp extends EventEmitter { return result === EmberStatus.SUCCESS; } + async leaveNetwork() { + var fut: Deferred, v, st; + fut = new Deferred(); + this.on('frame', (frameName: string, response: any) => { + if ((frameName === "stackStatusHandler")) { + fut.resolve(response); + } + }) + v = await this._command("leaveNetwork"); + if ((v[0] !== EmberStatus.SUCCESS)) { + this.logger("Failure to leave network:" + v); + throw new Error(("Failure to leave network:" + v)); + } + v = await fut.promise; + if ((v !== EmberStatus.NETWORK_DOWN)) { + this.logger("Failure to leave network:" + v); + throw new Error(("Failure to leave network:" + v)); + } + return v; + } + async setConfigurationValue(configId: number, value: any) { let ret; [ret] = await this.execCommand('setConfigurationValue', configId, value); @@ -114,6 +136,16 @@ export class Ezsp extends EventEmitter { return [ret]; } + async updatePolicies(zigpy_config: {}) { + // Set up the policies for what the NCP should do. + // policies = self.SCHEMAS[CONF_EZSP_POLICIES](zigpy_config[CONF_EZSP_POLICIES]) + // this.tc_policy = policies[EzspPolicyId.TRUST_CENTER_POLICY]; + + // for policy, value in policies.items(): + // [status] = await this.setPolicy(EzspPolicyId[policy], value); + // console.assert(status == EmberStatus.SUCCESS); + } + close() { return this._port.close(); } @@ -158,19 +190,19 @@ export class Ezsp extends EventEmitter { } }) v = await this._command("formNetwork", parameters); - if ((v[0] !== 0)) { + if ((v[0] !== EmberStatus.SUCCESS)) { this.logger("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } v = await fut.promise; - if ((v !== 0x90 /*EmberStatus.NETWORK_UP*/)) { + if ((v !== EmberStatus.NETWORK_UP)) { this.logger("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } return v; } - execCommand(name: string, ...args: any[]) { + execCommand(name: string, ...args: any[]): any { if (Object.keys(COMMANDS).indexOf(name) < 0) { throw new Error('Unknown command: ' + name); } From e9cb6989bde444c2fc3c1f8de5fc831dcdd731d2 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 18 Jan 2021 21:07:20 +0300 Subject: [PATCH 07/63] try to make socket support --- src/adapter/ezsp/driver/ezsp.ts | 5 +- src/adapter/ezsp/driver/uart.ts | 131 +++++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index a82205bd46..8326e6d765 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -11,7 +11,6 @@ import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; export class Ezsp extends EventEmitter { ezsp_version = 4; _gw: UartProtocol; - _port : any; _seq = 0; _tc_policy: any; _awaiting = new Map }>(); @@ -30,7 +29,7 @@ export class Ezsp extends EventEmitter { async connect(device: string, options: {}) { console.assert(!this._gw); - [this._gw, this._port] = await UartProtocol.connect(device, options, this.logger); + this._gw = await UartProtocol.connect(device, options, this.logger); this.startReadQueue(); } @@ -147,7 +146,7 @@ export class Ezsp extends EventEmitter { } close() { - return this._port.close(); + return this._gw.close(); } private _ezsp_frame(name: string, ...args: any[]) { diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 160722a3ad..5529e81320 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -1,5 +1,8 @@ -import * as SerialPort from 'serialport' +import SerialPort from 'serialport'; +import net from "net"; import { Deferred, AsyncQueue, crc16ccitt } from './utils'; +import SerialPortUtils from "../../serialPortUtils"; +import SocketPortUtils from "../../socketPortUtils"; const FLAG = 0x7E // Marks end of frame const ESCAPE = 0x7D @@ -37,12 +40,12 @@ export class UartProtocol implements AsyncIterable { _sendq: AsyncQueue<{ data: Buffer, seq: number }> _connected_future: Function | undefined _readq: AsyncQueue; - _transport: SerialPort; + // _transport: SerialPort | net.Socket; _nextIterator: { next: Function }; _dataFrameReceived: { next: Function }; logger: any; - constructor(private writeCb: (data: Buffer) => Promise, logger: any) { + constructor(private writeCb: (data: Buffer) => Promise, private closeCb: () => void, logger: any) { this.logger = logger; this._pending = [(- 1), null]; this._sendq = new AsyncQueue<{ data: Buffer, seq: number }>((iter: { next: Function }) => { @@ -189,6 +192,7 @@ export class UartProtocol implements AsyncIterable { reset() { /* Sends a reset frame */ + this.logger('uart reseting'); if ((this._reset_deferred)) { throw new TypeError("reset can only be called on a new connection"); } @@ -197,6 +201,11 @@ export class UartProtocol implements AsyncIterable { return this._reset_deferred.promise; } + close() { + this.logger('uart closing'); + return this.closeCb(); + } + private sleep(ms: number) { return new Promise(resolve => { setTimeout(resolve, ms) @@ -345,42 +354,88 @@ export class UartProtocol implements AsyncIterable { return out; } - static connect(portAddress: string, connectionOptions: {}, logger: any): Promise<[UartProtocol, SerialPort]> { - const SerialPort = require('serialport'); - const port = new SerialPort(portAddress, connectionOptions); - const protocol = new UartProtocol((data: Buffer) => { - //console.log('Writing to port', portAddress, data.toString('hex')); - return new Promise((resolve, reject) => { - port.write(data, (err: Error) => { - if (!err) { - resolve(); - } else { - reject(err) + static connect(portAddress: string, connectionOptions: {}, logger: any): Promise { + const portType = SocketPortUtils.isTcpPath(portAddress) ? 'socket' : 'serial'; + let protocol: UartProtocol; + if (portType == 'serial') { + const port = new SerialPort(portAddress, connectionOptions); + protocol = new UartProtocol((data: Buffer) => { + return new Promise((resolve, reject) => { + port.write(data, (err: Error) => { + if (!err) { + resolve(); + } else { + reject(err) + } + }); + }) + }, + () => { + return port.close(); + }, + logger + ); + + port.on('data', (data: any) => protocol.data_received(data)); + + return new Promise((resolve, reject) => { + port.on('open', () => { + logger('port open. resetting'); + protocol.reset().then( + () => { + logger('successfully reset'); + resolve(protocol); + }, (err) => { + logger(err); + reject() } - }); - }) - }, - logger - ); - - port.on('data', (data: any) => protocol.data_received(data)) - - return new Promise((resolve, reject) => { - port.on('open', () => { - logger('port open. resetting'); - protocol.reset().then( - () => { - logger('successfully reset'); - resolve([protocol, port]); - }, (err) => { - logger(err); - reject() - } - ); - }, (err: any) => { - logger(err); - reject() + ); + }); + port.on('error', (err: any) => { + logger(err); + reject(); + }); }); - }) + } else { // socket + const info = SocketPortUtils.parseTcpPath(portAddress); + logger(`Opening TCP socket with ${info.host}:${info.port}`); + const socketPort = new net.Socket(); + socketPort.setNoDelay(true); + socketPort.setKeepAlive(true, 15000); + protocol = new UartProtocol((data: Buffer) => { + return new Promise((resolve, reject) => { + socketPort.write(data, (err: Error) => { + if (!err) { + resolve(); + } else { + reject(err) + } + }); + }) + }, + () => { + return socketPort.emit('close'); + }, + logger + ); + // const parser = socketPort.pipe( + // new SerialPort.parsers.ByteLength({length: 1}), + // ); + socketPort.on('data', (data: any) => protocol.data_received(data)); + return new Promise((resolve, reject) => { + socketPort.on('connect', function () { + logger('Socket connected'); + }); + socketPort.on('ready', () => { + logger('socket open'); + resolve(protocol); + }); + socketPort.on('error', (err: any) => { + logger(err); + reject(); + }); + socketPort.connect(info.port, info.host); + }); + } } } From 4d5e70ac985ff703c37adb0c8dd1590dbc4a472f Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 18 Jan 2021 21:11:21 +0300 Subject: [PATCH 08/63] forgoten reset on connect --- src/adapter/ezsp/driver/uart.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 5529e81320..172123e568 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -428,7 +428,15 @@ export class UartProtocol implements AsyncIterable { }); socketPort.on('ready', () => { logger('socket open'); - resolve(protocol); + protocol.reset().then( + () => { + logger('successfully reset'); + resolve(protocol); + }, (err) => { + logger(err); + reject() + } + ); }); socketPort.on('error', (err: any) => { logger(err); From ef0de09ff7404692989ef188463ddef9b85fcbdd Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 19 Jan 2021 22:06:07 +0300 Subject: [PATCH 09/63] permit join --- src/adapter/ezsp/adapter/ezspAdapter.ts | 44 ++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 318a720634..6b9e855968 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -52,6 +52,12 @@ class EZSPAdapter extends Adapter { // todo let [nwk, ieee] = arr; debug('Device join request received: %s %s', nwk, ieee); + const payload: Events.DeviceJoinedPayload = { + networkAddress: nwk, + ieeeAddr: ieee, + }; + + this.emit(Events.Events.deviceJoined, payload); // let devices = this.getDevices(); // if (!devices.some(d => d.nodeId === nwk || d.eui64 === ieee.toString())) { // devices.push({ nodeId: nwk, eui64: ieee.toString() }); @@ -113,6 +119,8 @@ class EZSPAdapter extends Adapter { public async permitJoin(seconds: number, networkAddress: number): Promise { // todo + await this.driver.permitJoining(seconds); + return Promise.resolve(); } public async getCoordinatorVersion(): Promise { @@ -144,9 +152,43 @@ class EZSPAdapter extends Adapter { public async nodeDescriptor(networkAddress: number): Promise { // todo + // return this.queue.execute(async () => { + // this.checkInterpanLock(); + // try { + // const result = await this.nodeDescriptorInternal(networkAddress); + // return result; + // } catch (error) { + // debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); + // // Doing a route discovery after simple descriptor request fails makes it succeed sometimes. + // // https://github.com/Koenkk/zigbee2mqtt/issues/3276 + // await this.discoverRoute(networkAddress); + // const result = await this.nodeDescriptorInternal(networkAddress); + // return result; + // } + // }, networkAddress); return Promise.reject(); } + // private async nodeDescriptorInternal(networkAddress: number): Promise { + // const response = this.znp.waitFor(Type.AREQ, Subsystem.ZDO, 'nodeDescRsp', {nwkaddr: networkAddress}); + // const payload = {dstaddr: networkAddress, nwkaddrofinterest: networkAddress}; + // await this.znp.request(Subsystem.ZDO, 'nodeDescReq', payload, response.ID); + // const descriptor = await response.start().promise; + + // let type: DeviceType = 'Unknown'; + // const logicalType = descriptor.payload.logicaltype_cmplxdescavai_userdescavai & 0x07; + // for (const [key, value] of Object.entries(Constants.ZDO.deviceLogicalType)) { + // if (value === logicalType) { + // if (key === 'COORDINATOR') type = 'Coordinator'; + // else if (key === 'ROUTER') type = 'Router'; + // else if (key === 'ENDDEVICE') type = 'EndDevice'; + // break; + // } + // } + + // return {manufacturerCode: descriptor.payload.manufacturercode, type}; + // } + public async activeEndpoints(networkAddress: number): Promise { // todo return Promise.reject(); @@ -180,7 +222,7 @@ class EZSPAdapter extends Adapter { public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { // todo - return Promise.reject(); + return Promise.resolve(); } public async bind( From 70d91c4e698393d4e5a8335662566128b7dc6032 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 21 Jan 2021 23:19:59 +0300 Subject: [PATCH 10/63] next step --- src/adapter/ezsp/adapter/ezspAdapter.ts | 85 +++++++++++++++---------- src/adapter/ezsp/driver/commands.ts | 8 ++- src/adapter/ezsp/driver/driver.ts | 12 +++- src/adapter/ezsp/driver/ezsp.ts | 6 ++ src/adapter/ezsp/driver/types/index.ts | 6 +- src/adapter/ezsp/driver/types/named.ts | 77 ++++++++++++++++++++++ src/adapter/ezsp/driver/types/struct.ts | 13 ++++ 7 files changed, 167 insertions(+), 40 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 6b9e855968..f939620897 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -9,6 +9,8 @@ import Debug from "debug"; import Adapter from '../../adapter'; const debug = Debug("zigbee-herdsman:ezsp:adapter"); import {Ezsp, Driver} from '../driver'; +import { EmberApsFrame } from '../driver/types/struct'; +import { EmberZDOCmd, EmberApsOption } from '../driver/types/named'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; import * as Zcl from '../../../zcl'; @@ -29,10 +31,12 @@ interface WaitressMatcher { class EZSPAdapter extends Adapter { private driver: Driver; private port: SerialPortOptions; + private transactionID: number; public constructor(networkOptions: NetworkOptions, serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { super(networkOptions, serialPortOptions, backupPath, adapterOptions); + this.transactionID = 1; this.port = serialPortOptions; this.driver = new Driver(); this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); @@ -40,6 +44,16 @@ class EZSPAdapter extends Adapter { this.driver.on('incomingMessage', this.processMessage.bind(this)); } + private nextTransactionID(): number { + this.transactionID++; + + if (this.transactionID > 255) { + this.transactionID = 1; + } + + return this.transactionID; + } + private async processMessage(frame: any) { // todo if (!frame.senderEui64) { @@ -152,42 +166,45 @@ class EZSPAdapter extends Adapter { public async nodeDescriptor(networkAddress: number): Promise { // todo - // return this.queue.execute(async () => { - // this.checkInterpanLock(); - // try { - // const result = await this.nodeDescriptorInternal(networkAddress); - // return result; - // } catch (error) { - // debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); - // // Doing a route discovery after simple descriptor request fails makes it succeed sometimes. - // // https://github.com/Koenkk/zigbee2mqtt/issues/3276 - // await this.discoverRoute(networkAddress); - // const result = await this.nodeDescriptorInternal(networkAddress); - // return result; - // } - // }, networkAddress); - return Promise.reject(); + try { + debug(`Request Node descriptor for '${networkAddress}'`); + const result = await this.nodeDescriptorInternal(networkAddress); + return result; + } catch (error) { + debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); + throw error; + } } - // private async nodeDescriptorInternal(networkAddress: number): Promise { - // const response = this.znp.waitFor(Type.AREQ, Subsystem.ZDO, 'nodeDescRsp', {nwkaddr: networkAddress}); - // const payload = {dstaddr: networkAddress, nwkaddrofinterest: networkAddress}; - // await this.znp.request(Subsystem.ZDO, 'nodeDescReq', payload, response.ID); - // const descriptor = await response.start().promise; - - // let type: DeviceType = 'Unknown'; - // const logicalType = descriptor.payload.logicaltype_cmplxdescavai_userdescavai & 0x07; - // for (const [key, value] of Object.entries(Constants.ZDO.deviceLogicalType)) { - // if (value === logicalType) { - // if (key === 'COORDINATOR') type = 'Coordinator'; - // else if (key === 'ROUTER') type = 'Router'; - // else if (key === 'ENDDEVICE') type = 'EndDevice'; - // break; - // } - // } - - // return {manufacturerCode: descriptor.payload.manufacturercode, type}; - // } + private async nodeDescriptorInternal(networkAddress: number): Promise { + //const response = this.driver.waitFor(Type.AREQ, Subsystem.ZDO, 'nodeDescRsp', {nwkaddr: networkAddress}); + //const payload = {dstaddr: networkAddress, nwkaddrofinterest: networkAddress}; + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Node_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + + await this.driver.request(networkAddress, frame, payload); + //const descriptor = await response.start().promise; + + // let type: DeviceType = 'Unknown'; + // const logicalType = descriptor.payload.logicaltype_cmplxdescavai_userdescavai & 0x07; + // for (const [key, value] of Object.entries(Constants.ZDO.deviceLogicalType)) { + // if (value === logicalType) { + // if (key === 'COORDINATOR') type = 'Coordinator'; + // else if (key === 'ROUTER') type = 'Router'; + // else if (key === 'ENDDEVICE') type = 'EndDevice'; + // break; + // } + // } + + return {manufacturerCode: 0, type: 'EndDevice'}; + } public async activeEndpoints(networkAddress: number): Promise { // todo diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index b89c4793f9..5c6f927ca4 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -24,7 +24,8 @@ import {/* Basic Types */ EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, + EmberNodeDescriptor, } from './types'; export const COMMANDS = { @@ -675,7 +676,10 @@ export const COMMANDS = { ], "gpepIncomingMessageHandler": [197, [], [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] - ] + ], + // EmberZDOCmd + "Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], + "Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], }; //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 7679e9ef9f..e4252c3183 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -184,13 +184,14 @@ export class Driver extends EventEmitter { public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { - let seq = apsFrame.sequence; + let seq = apsFrame.sequence+1; console.assert(!this.pending.has(seq)); let sendDeferred = new Deferred(); let replyDeferred = new Deferred(); this.pending.set(seq, [sendDeferred, replyDeferred]); let handle; + let eui64, st; try { if (timeout > 0) { @@ -201,7 +202,7 @@ export class Driver extends EventEmitter { if (typeof nwk !== 'number') { - let eui64 = nwk as EmberEUI64; + eui64 = nwk as EmberEUI64; let strEui64 = eui64.toString(); let nodeId = this.eui64ToNodeId.get(strEui64); if (nodeId === undefined) { @@ -213,7 +214,10 @@ export class Driver extends EventEmitter { } } nwk = nodeId; + } else { + [st, eui64] = await this._ezsp.execCommand('lookupEui64ByNodeId', nwk); } + await this._ezsp.execCommand('setExtendedTimeout', eui64, true); let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); console.log('unicast message sent, waiting for reply'); @@ -299,4 +303,8 @@ export class Driver extends EventEmitter { public permitJoining(seconds:number){ return this._ezsp.execCommand('permitJoining', seconds); } + + public make_zdo_frame(name: string, ...args: any[]) { + return this._ezsp.make_zdo_frame(name, ...args); + } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 8326e6d765..79bce2142b 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -149,6 +149,12 @@ export class Ezsp extends EventEmitter { return this._gw.close(); } + public make_zdo_frame(name: string, ...args: any[]): Buffer { + var c, data, frame, cmd_id; + c = (COMMANDS)[name]; + data = t.serialize(args, c[1]); + return data; + } private _ezsp_frame(name: string, ...args: any[]) { var c, data, frame, cmd_id; c = (COMMANDS)[name]; diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 5fa37b371a..39faf77720 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -30,7 +30,8 @@ import { EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, + EmberNodeDescriptor, } from './struct' export function deserialize(payload: any, schema: any[]) { @@ -78,5 +79,6 @@ export { EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry + EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, + EmberNodeDescriptor } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index f8b2984272..77c2beb433 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -1653,4 +1653,81 @@ export class EmberNetworkInitBitmask extends basic.uint16_t { // Save parent info (node ID and EUI64) in a token during joining/rejoin, // and restore on reboot. static NETWORK_INIT_PARENT_INFO_IN_TOKEN = 0x0001 +} + +export class EmberZDOCmd extends basic.uint16_t { + // Device and Service Discovery Server Requests + static NWK_addr_req = 0x0000 + static IEEE_addr_req = 0x0001 + static Node_Desc_req = 0x0002 + static Power_Desc_req = 0x0003 + static Simple_Desc_req = 0x0004 + static Active_EP_req = 0x0005 + static Match_Desc_req = 0x0006 + static Complex_Desc_req = 0x0010 + static User_Desc_req = 0x0011 + static Discovery_Cache_req = 0x0012 + static Device_annce = 0x0013 + static User_Desc_set = 0x0014 + static System_Server_Discovery_req = 0x0015 + static Discovery_store_req = 0x0016 + static Node_Desc_store_req = 0x0017 + static Active_EP_store_req = 0x0019 + static Simple_Desc_store_req = 0x001A + static Remove_node_cache_req = 0x001B + static Find_node_cache_req = 0x001C + static Extended_Simple_Desc_req = 0x001D + static Extended_Active_EP_req = 0x001E + static Parent_annce = 0x001F + // Bind Management Server Services Responses + static End_Device_Bind_req = 0x0020 + static Bind_req = 0x0021 + static Unbind_req = 0x0022 + // Network Management Server Services Requests + // ... TODO optional stuff ... + static Mgmt_Lqi_req = 0x0031 + static Mgmt_Rtg_req = 0x0032 + // ... TODO optional stuff ... + static Mgmt_Leave_req = 0x0034 + static Mgmt_Permit_Joining_req = 0x0036 + static Mgmt_NWK_Update_req = 0x0038 + // ... TODO optional stuff ... + + // Responses + // Device and Service Discovery Server Responses + static NWK_addr_rsp = 0x8000 + static IEEE_addr_rsp = 0x8001 + static Node_Desc_rsp = 0x8002 + static Power_Desc_rsp = 0x8003 + static Simple_Desc_rsp = 0x8004 + static Active_EP_rsp = 0x8005 + static Match_Desc_rsp = 0x8006 + static Complex_Desc_rsp = 0x8010 + static User_Desc_rsp = 0x8011 + static Discovery_Cache_rsp = 0x8012 + static User_Desc_conf = 0x8014 + static System_Server_Discovery_rsp = 0x8015 + static Discovery_Store_rsp = 0x8016 + static Node_Desc_store_rsp = 0x8017 + static Power_Desc_store_rsp = 0x8018 + static Active_EP_store_rsp = 0x8019 + static Simple_Desc_store_rsp = 0x801A + static Remove_node_cache_rsp = 0x801B + static Find_node_cache_rsp = 0x801C + static Extended_Simple_Desc_rsp = 0x801D + static Extended_Active_EP_rsp = 0x801E + static Parent_annce_rsp = 0x801F + // Bind Management Server Services Responses + static End_Device_Bind_rsp = 0x8020 + static Bind_rsp = 0x8021 + static Unbind_rsp = 0x8022 + // ... TODO optional stuff ... + // Network Management Server Services Responses + static Mgmt_Lqi_rsp = 0x8031 + static Mgmt_Rtg_rsp = 0x8032 + // ... TODO optional stuff ... + static Mgmt_Leave_rsp = 0x8034 + static Mgmt_Permit_Joining_rsp = 0x8036 + // ... TODO optional stuff ... + static Mgmt_NWK_Update_rsp = 0x8038 } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 17c9fdd2de..1f40bf39f4 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -594,4 +594,17 @@ export class EmberGpSinkListEntry extends EzspStruct { ] } +export class EmberNodeDescriptor extends EzspStruct { + static _fields = [ + ['byte1', basic.uint8_t], + ['byte2', basic.uint8_t], + ['mac_capability_flags', basic.uint8_t], + ['manufacturer_code', basic.uint16_t], + ['maximum_buffer_size', basic.uint8_t], + ['maximum_incoming_transfer_size', basic.uint16_t], + ['server_mask', basic.uint16_t], + ['maximum_outgoing_transfer_size', basic.uint16_t], + ['descriptor_capability_field', basic.uint8_t], + ] +} From 38529aadfca7ea22b2077a079ef28e9b2796bc76 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 22 Jan 2021 18:12:15 +0300 Subject: [PATCH 11/63] polices and other --- src/adapter/ezsp/adapter/ezspAdapter.ts | 2 +- src/adapter/ezsp/driver/commands.ts | 13 +++++++++++++ src/adapter/ezsp/driver/driver.ts | 26 +++++++++++++++++++++++++ src/adapter/ezsp/driver/ezsp.ts | 16 +++++++++------ src/adapter/ezsp/driver/types/basic.ts | 7 +++++++ src/adapter/ezsp/driver/types/index.ts | 4 +++- src/adapter/ezsp/driver/types/named.ts | 23 ++++++++++++++++++++-- 7 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index f939620897..4b269c406e 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -167,7 +167,7 @@ class EZSPAdapter extends Adapter { public async nodeDescriptor(networkAddress: number): Promise { // todo try { - debug(`Request Node descriptor for '${networkAddress}'`); + debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); const result = await this.nodeDescriptorInternal(networkAddress); return result; } catch (error) { diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 5c6f927ca4..b186764bdc 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -6,6 +6,7 @@ import {/* Basic Types */ uint32_t, LVBytes, fixed_list, + WordList, /* Named Types */ EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberNodeId, @@ -38,6 +39,18 @@ export const COMMANDS = { "setConfigurationValue": [83, [EzspConfigId, uint16_t], [EzspStatus] ], + "addEndpoint": [0x0002, [ + uint8_t, + uint16_t, + uint16_t, + uint8_t, + uint8_t, + uint8_t, + WordList, + WordList, + ], + [EzspStatus], + ], "setPolicy": [85, [EzspPolicyId, EzspDecisionId], [EzspStatus] ], diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index e4252c3183..22fc5abe8f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -7,6 +7,15 @@ import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId } from './types/named'; import { Multicast } from './multicast'; +interface AddEndpointParameters { + endpoint?: number, + profileId?: number, + deviceId?: number, + appFlags?: number, + inputClusters?: number[], + outputClusters?: number[], +}; + export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT private _ezsp: Ezsp; @@ -55,11 +64,14 @@ export class Driver extends EventEmitter { await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); + await this.addEndpoint({outputClusters: [0x0500]}); + if (!await ezsp.networkInit()) { await this.form_network(); const state = await ezsp.execCommand('networkState'); console.log('Network state', state); } + let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); console.assert(status == EmberStatus.SUCCESS); if (nodeType != EmberNodeType.COORDINATOR) { @@ -307,4 +319,18 @@ export class Driver extends EventEmitter { public make_zdo_frame(name: string, ...args: any[]) { return this._ezsp.make_zdo_frame(name, ...args); } + + public async addEndpoint({endpoint=1, profileId=260, deviceId=0xBEEF, appFlags=0, inputClusters=[], outputClusters=[]}: AddEndpointParameters) { + const res = await this._ezsp.execCommand('addEndpoint', + endpoint, + profileId, + deviceId, + appFlags, + inputClusters.length, + outputClusters.length, + inputClusters, + outputClusters, + ); + this.logger("Ezsp adding endpoint: %s", res); + } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 79bce2142b..a04cd95aed 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -3,7 +3,7 @@ import { UartProtocol } from './uart'; import { COMMANDS } from './commands'; import { Deferred } from './utils'; -import { EmberStatus, EmberOutgoingMessageType } from './types/named'; +import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; @@ -137,12 +137,16 @@ export class Ezsp extends EventEmitter { async updatePolicies(zigpy_config: {}) { // Set up the policies for what the NCP should do. - // policies = self.SCHEMAS[CONF_EZSP_POLICIES](zigpy_config[CONF_EZSP_POLICIES]) - // this.tc_policy = policies[EzspPolicyId.TRUST_CENTER_POLICY]; + const policies = [ + [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], + [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], + [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], + ]; - // for policy, value in policies.items(): - // [status] = await this.setPolicy(EzspPolicyId[policy], value); - // console.assert(status == EmberStatus.SUCCESS); + for (let [policy, value] of policies) { + const [status] = await this.execCommand('setPolicy', policy, value); + console.assert(status == EmberStatus.SUCCESS); + } } close() { diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index 778a4411a5..1d9444ad29 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -143,6 +143,13 @@ export function LVList(itemtype: any) : List { return LVList; } +export class WordList extends List { + static serialize(cls: any, value: any[]) { + const data = value.map(i => Buffer.from(uint16_t.serialize(uint16_t, i))); + return Buffer.concat(data); + } +} + class _FixedList extends List { static serialize(cls: any, value: any[]) { const data = value.map(i => cls._itemtype.serialize(cls._itemtype, i)[0]); diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 39faf77720..0524f4562f 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -9,7 +9,8 @@ import { LVBytes, list, LVList, - fixed_list + fixed_list, + WordList, } from './basic'; import { @@ -61,6 +62,7 @@ export { list, LVList, fixed_list, + WordList, /* Named Types */ NcpResetCode, EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberRf4ceApplicationCapabilities, EmberNodeId, diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 77c2beb433..87f62b460b 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -62,7 +62,7 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { } console.assert(cls._length === value.length); const val = (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)); - return Buffer.from(val as any[]); + return Buffer.concat(val); } public get value() { @@ -452,7 +452,7 @@ export class EzspPolicyId extends basic.uint8_t { } -export class EzspDecisionId extends basic.uint8_t { +export class EzspDecisionId extends basic.uint16_t { // Identifies a policy decision. // Send the network key in the clear to all joining and rejoining devices. @@ -1730,4 +1730,23 @@ export class EmberZDOCmd extends basic.uint16_t { static Mgmt_Permit_Joining_rsp = 0x8036 // ... TODO optional stuff ... static Mgmt_NWK_Update_rsp = 0x8038 +} + +export class EzspDecisionBitmask extends basic.uint16_t { + // EZSP Decision bitmask. + + // Disallow joins and rejoins. + static DEFAULT_CONFIGURATION = 0x0000 + // Send the network key to all joining devices. + static ALLOW_JOINS = 0x0001 + // Send the network key to all rejoining devices. + static ALLOW_UNSECURED_REJOINS = 0x0002 + // Send the network key in the clear. + static SEND_KEY_IN_CLEAR = 0x0004 + // Do nothing for unsecured rejoins. + static IGNORE_UNSECURED_REJOINS = 0x0008 + // Allow joins if there is an entry in the transient key table. + static JOINS_USE_INSTALL_CODE_KEY = 0x0010 + // Delay sending the network key to a new joining device. + DEFER_JOINS = 0x0020 } \ No newline at end of file From cc76a56eb874a142eefd8b20d2d127dd96220ded Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 24 Jan 2021 22:28:32 +0300 Subject: [PATCH 12/63] step --- src/adapter/ezsp/driver/driver.ts | 13 +++++++++++-- src/adapter/ezsp/driver/types/named.ts | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 22fc5abe8f..69d0cf917c 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -6,6 +6,7 @@ import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId } from './types/named'; import { Multicast } from './multicast'; +import Waitress from "../../../utils/waitress"; interface AddEndpointParameters { endpoint?: number, @@ -27,6 +28,7 @@ export class Driver extends EventEmitter { private _nwk: EmberNodeId; private _ieee: EmberEUI64; private _multicast: Multicast; + //private waitress: Waitress; constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ super(); @@ -203,7 +205,7 @@ export class Driver extends EventEmitter { this.pending.set(seq, [sendDeferred, replyDeferred]); let handle; - let eui64, st; + let eui64: EmberEUI64; try { if (timeout > 0) { @@ -227,7 +229,7 @@ export class Driver extends EventEmitter { } nwk = nodeId; } else { - [st, eui64] = await this._ezsp.execCommand('lookupEui64ByNodeId', nwk); + eui64 = await this.networkIdToEUI64(nwk); } await this._ezsp.execCommand('setExtendedTimeout', eui64, true); @@ -333,4 +335,11 @@ export class Driver extends EventEmitter { ); this.logger("Ezsp adding endpoint: %s", res); } + + // public waitFor( + // type: Type, subsystem: Subsystem, command: string, payload: ZpiObjectPayload = {}, + // timeout: number = timeouts.default + // ): {start: () => {promise: Promise; ID: number}; ID: number} { + // return this.waitress.waitFor({type, subsystem, command, payload}, timeout); + // } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 87f62b460b..6bc18eaf86 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -61,8 +61,8 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { value = (value as EmberEUI64).value as number[]; } console.assert(cls._length === value.length); - const val = (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)); - return Buffer.concat(val); + const val = (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)[0]); + return Buffer.from(val); } public get value() { From e28c1edc0de6893f05a5033fce1cbda9f0b8ad08 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 25 Jan 2021 23:09:54 +0300 Subject: [PATCH 13/63] step --- src/adapter/ezsp/adapter/ezspAdapter.ts | 9 ++++++++- src/adapter/ezsp/driver/driver.ts | 10 +++++++--- src/adapter/ezsp/driver/types/index.ts | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 4b269c406e..6c84a90997 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -10,7 +10,7 @@ import Adapter from '../../adapter'; const debug = Debug("zigbee-herdsman:ezsp:adapter"); import {Ezsp, Driver} from '../driver'; import { EmberApsFrame } from '../driver/types/struct'; -import { EmberZDOCmd, EmberApsOption } from '../driver/types/named'; +import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; import * as Zcl from '../../../zcl'; @@ -56,9 +56,16 @@ class EZSPAdapter extends Adapter { private async processMessage(frame: any) { // todo + debug(`processMessage: ${JSON.stringify(frame)}`); if (!frame.senderEui64) { frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) } + if (frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { + const [nwk, rest] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); + debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); + this.handleDeviceJoin([nwk, ieee, 0]); + } this.emit('event', frame); } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 69d0cf917c..eb486641fb 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -4,7 +4,7 @@ import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, E import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; import { Deferred, ember_security } from './utils'; -import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId } from './types/named'; +import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask } from './types/named'; import { Multicast } from './multicast'; import Waitress from "../../../utils/waitress"; @@ -95,6 +95,8 @@ export class Driver extends EventEmitter { // this.handle_join(this.nwk, this.ieee, 0); console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) + + //this.emit('deviceJoined', [nwk, this._ieee]); this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); // const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); @@ -314,8 +316,10 @@ export class Driver extends EventEmitter { } } - public permitJoining(seconds:number){ - return this._ezsp.execCommand('permitJoining', seconds); + public async permitJoining(seconds:number){ + const [status] = await this._ezsp.execCommand('setPolicy', EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); + console.assert(status == EmberStatus.SUCCESS); + return await this._ezsp.execCommand('permitJoining', seconds); } public make_zdo_frame(name: string, ...args: any[]) { diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 0524f4562f..f5d08a311c 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -21,7 +21,7 @@ import { EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, - EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask + EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, } from './named'; import { @@ -72,7 +72,7 @@ export { EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, - EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, + EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, /* Structs */ EzspStruct, EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, From 7c359fc781eb23a888b31bf8920df5e1dff6dc76 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 26 Jan 2021 18:42:03 +0300 Subject: [PATCH 14/63] small fixes --- src/adapter/ezsp/driver/commands.ts | 6 ++-- src/adapter/ezsp/driver/driver.ts | 44 +++++++++++++++++--------- src/adapter/ezsp/driver/ezsp.ts | 13 ++++++-- src/adapter/ezsp/driver/multicast.ts | 3 +- src/adapter/ezsp/driver/types/named.ts | 2 +- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index b186764bdc..09e2128d2b 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -690,9 +690,9 @@ export const COMMANDS = { "gpepIncomingMessageHandler": [197, [], [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] ], - // EmberZDOCmd - "Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], - "Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], }; +//// EmberZDOCmd +//"Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], +//"Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index eb486641fb..fb9f960d9f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,10 +1,10 @@ import * as TsType from './../../tstype'; import { Ezsp } from './ezsp'; -import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, EmberNodeId } from './types'; +import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; import { Deferred, ember_security } from './utils'; -import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask } from './types/named'; +import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId } from './types/named'; import { Multicast } from './multicast'; import Waitress from "../../../utils/waitress"; @@ -49,25 +49,41 @@ export class Driver extends EventEmitter { const version = await ezsp.version(); console.log('Got version', version); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SECURITY_LEVEL, 5); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS, EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SECURITY_LEVEL, 5); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 8); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); - + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); + await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); + this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + await this.addEndpoint({outputClusters: [0x0500]}); + // getting MFG_STRING token + const mfgName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_STRING); + // getting MFG_BOARD_NAME token + const boardName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_BOARD_NAME); + let verInfo = await ezsp.getValue(EzspValueId.VALUE_VERSION_INFO); + let build, major, minor, patch, special; + [build, verInfo] = uint16_t.deserialize(uint16_t, verInfo); + [major, verInfo] = uint8_t.deserialize(uint8_t, verInfo); + [minor, verInfo] = uint8_t.deserialize(uint8_t, verInfo); + [patch, verInfo] = uint8_t.deserialize(uint8_t, verInfo); + [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); + const vers = `${major}.${minor}.${patch}.${special} build ${build}`; + this.logger(`EmberZNet version: ${vers}`); + if (!await ezsp.networkInit()) { await this.form_network(); const state = await ezsp.execCommand('networkState'); @@ -87,7 +103,7 @@ export class Driver extends EventEmitter { this.networkParams = networkParams; this.logger("Node type: %s, Network parameters: %s", nodeType, networkParams); - await ezsp.updatePolicies({}); + await ezsp.updatePolicies(); const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; @@ -99,8 +115,6 @@ export class Driver extends EventEmitter { //this.emit('deviceJoined', [nwk, this._ieee]); this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); - // const [status, count] = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); - // this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); this._multicast = new Multicast(ezsp, logger); await this._multicast.startup([]); } diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index a04cd95aed..da7521b464 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -97,7 +97,7 @@ export class Ezsp extends EventEmitter { [ret, value] = await this.execCommand('getConfigurationValue', configId); console.assert(ret === EmberStatus.SUCCESS); this.logger('Get %s = %s', configId, value); - return [ret, value]; + return value; } async getMulticastTableEntry(index: number) { @@ -135,12 +135,19 @@ export class Ezsp extends EventEmitter { return [ret]; } - async updatePolicies(zigpy_config: {}) { + async getValue(valueId: t.EzspValueId) { + let ret, value; + [ret, value] = await this.execCommand('getValue', valueId); + console.assert(ret === EmberStatus.SUCCESS); + return value; + } + + async updatePolicies() { // Set up the policies for what the NCP should do. const policies = [ [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], - [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], + [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], ]; for (let [policy, value] of policies) { diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index ed8131f616..28005f53b4 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -20,10 +20,9 @@ export class Multicast { } private async _initialize() { - const [status, size] = await this._ezsp.getConfigurationValue( + const size = await this._ezsp.getConfigurationValue( EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE ); - if (status !== EmberStatus.SUCCESS) return; for (let i = 0; i < size; i++) { let st: any, entry: any; [st, entry] = await this._ezsp.getMulticastTableEntry(i); diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 6bc18eaf86..5a547835a6 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -1748,5 +1748,5 @@ export class EzspDecisionBitmask extends basic.uint16_t { // Allow joins if there is an entry in the transient key table. static JOINS_USE_INSTALL_CODE_KEY = 0x0010 // Delay sending the network key to a new joining device. - DEFER_JOINS = 0x0020 + static DEFER_JOINS = 0x0020 } \ No newline at end of file From d27f1360dd111e351de9722ad4e05da9c1c0a13d Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 26 Jan 2021 21:06:55 +0300 Subject: [PATCH 15/63] small fix --- src/adapter/ezsp/driver/commands.ts | 8 ++++++-- src/adapter/ezsp/driver/ezsp.ts | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 09e2128d2b..9b5ffbf1d0 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -690,9 +690,13 @@ export const COMMANDS = { "gpepIncomingMessageHandler": [197, [], [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] ], + "changeSourceRouteHandler": [196, [], [EmberNodeId, EmberNodeId]], //Bool }; + //// EmberZDOCmd -//"Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], -//"Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], +export const ZDO_COMMANDS = { + "Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], + "Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], +} //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index da7521b464..5beccdd6c0 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,6 +1,6 @@ import * as t from './types'; import { UartProtocol } from './uart'; -import { COMMANDS } from './commands'; +import { COMMANDS, ZDO_COMMANDS } from './commands'; import { Deferred } from './utils'; import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; @@ -162,7 +162,7 @@ export class Ezsp extends EventEmitter { public make_zdo_frame(name: string, ...args: any[]): Buffer { var c, data, frame, cmd_id; - c = (COMMANDS)[name]; + c = (ZDO_COMMANDS)[name]; data = t.serialize(args, c[1]); return data; } @@ -249,14 +249,14 @@ export class Ezsp extends EventEmitter { let cmd = this.COMMANDS_BY_ID.get(frame_id); if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); let frameName = cmd.name; - this.logger("Application frame %s (%s) received", frame_id, frameName); + this.logger("Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); if (this._awaiting.has(sequence)) { let entry = this._awaiting.get(sequence); this._awaiting.delete(sequence); if (entry) { console.assert(entry.expectedId === frame_id); [result, data] = t.deserialize(data, entry.schema); - this.logger(`Application frame ${frame_id} (${frameName}): ${result}`); + this.logger(`Application frame ${frame_id} (${frameName}) parsed: ${result}`); entry.deferred.resolve(result); } } else { From a36334c3edd52308201c416013807bb7293f294d Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 26 Jan 2021 22:40:31 +0300 Subject: [PATCH 16/63] next --- src/adapter/ezsp/adapter/ezspAdapter.ts | 12 +++++------- src/adapter/ezsp/driver/driver.ts | 9 +++++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 6c84a90997..a763a39e36 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -78,13 +78,11 @@ class EZSPAdapter extends Adapter { ieeeAddr: ieee, }; - this.emit(Events.Events.deviceJoined, payload); - // let devices = this.getDevices(); - // if (!devices.some(d => d.nodeId === nwk || d.eui64 === ieee.toString())) { - // devices.push({ nodeId: nwk, eui64: ieee.toString() }); - // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); - // log.info('Added device to DB: %s %s', nwk, ieee) - // } + if (nwk == 0) { + const nd = this.nodeDescriptor(nwk); + } else { + this.emit(Events.Events.deviceJoined, payload); + } } private handleDeviceLeft(arr: any[]) { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index fb9f960d9f..59188a004d 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -107,12 +107,13 @@ export class Driver extends EventEmitter { const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; - this._ieee = await this.getLocalEUI64(); - // this.handle_join(this.nwk, this.ieee, 0); + const [ieee] = await this._ezsp.execCommand('getEui64'); + this._ieee = ieee; console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) - //this.emit('deviceJoined', [nwk, this._ieee]); + await this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); + this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); this._multicast = new Multicast(ezsp, logger); @@ -247,7 +248,7 @@ export class Driver extends EventEmitter { } else { eui64 = await this.networkIdToEUI64(nwk); } - await this._ezsp.execCommand('setExtendedTimeout', eui64, true); + //await this._ezsp.execCommand('setExtendedTimeout', eui64, true); let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); console.log('unicast message sent, waiting for reply'); From 2b1f956ae75884f9aa5c6c52b8928768428ab56b Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 28 Jan 2021 17:55:53 +0300 Subject: [PATCH 17/63] working with request --- src/adapter/ezsp/adapter/ezspAdapter.ts | 23 +--- src/adapter/ezsp/driver/driver.ts | 152 +++++++++++++++------- src/adapter/ezsp/driver/ezsp.ts | 19 ++- src/adapter/ezsp/driver/types/emberObj.ts | 30 +++++ src/adapter/ezsp/driver/uart.ts | 7 +- 5 files changed, 158 insertions(+), 73 deletions(-) create mode 100644 src/adapter/ezsp/driver/types/emberObj.ts diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index a763a39e36..3f7af712b3 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -182,8 +182,6 @@ class EZSPAdapter extends Adapter { } private async nodeDescriptorInternal(networkAddress: number): Promise { - //const response = this.driver.waitFor(Type.AREQ, Subsystem.ZDO, 'nodeDescRsp', {nwkaddr: networkAddress}); - //const payload = {dstaddr: networkAddress, nwkaddrofinterest: networkAddress}; const frame = new EmberApsFrame(); frame.clusterId = EmberZDOCmd.Node_Desc_req; frame.profileId = 0; @@ -192,23 +190,14 @@ class EZSPAdapter extends Adapter { frame.destinationEndpoint = 0; frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - await this.driver.request(networkAddress, frame, payload); - //const descriptor = await response.start().promise; - - // let type: DeviceType = 'Unknown'; - // const logicalType = descriptor.payload.logicaltype_cmplxdescavai_userdescavai & 0x07; - // for (const [key, value] of Object.entries(Constants.ZDO.deviceLogicalType)) { - // if (value === logicalType) { - // if (key === 'COORDINATOR') type = 'Coordinator'; - // else if (key === 'ROUTER') type = 'Router'; - // else if (key === 'ENDDEVICE') type = 'EndDevice'; - // break; - // } - // } - - return {manufacturerCode: 0, type: 'EndDevice'}; + const descriptor = await response.start().promise; + debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); + const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); + debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); + return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; } public async activeEndpoints(networkAddress: number): Promise { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 59188a004d..936219389d 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -3,6 +3,7 @@ import { Ezsp } from './ezsp'; import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; +import { EmberObject } from './types/emberObj'; import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId } from './types/named'; import { Multicast } from './multicast'; @@ -17,6 +18,14 @@ interface AddEndpointParameters { outputClusters?: number[], }; +type EmberObjectPayload = any; + +type EmberWaitressMatcher = { + address: number, + clusterId: number +}; + + export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT private _ezsp: Ezsp; @@ -28,17 +37,20 @@ export class Driver extends EventEmitter { private _nwk: EmberNodeId; private _ieee: EmberEUI64; private _multicast: Multicast; - //private waitress: Waitress; + private waitress: Waitress; constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ super(); - if (!nodeInfo) return; + // if (!nodeInfo) return; - for(let node of nodeInfo){ - let eui64 = node.eui64 instanceof EmberEUI64 ? node.eui64 : new EmberEUI64(node.eui64); - this.eui64ToNodeId.set(eui64.toString(), node.nodeId); - } + // for(let node of nodeInfo){ + // let eui64 = node.eui64 instanceof EmberEUI64 ? node.eui64 : new EmberEUI64(node.eui64); + // this.eui64ToNodeId.set(eui64.toString(), node.nodeId); + // } + + this.waitress = new Waitress( + this.waitressValidator, this.waitressTimeoutFormatter); } public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions, logger: any) { @@ -112,7 +124,7 @@ export class Driver extends EventEmitter { console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) - await this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); + this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); @@ -151,6 +163,9 @@ export class Driver extends EventEmitter { break; } } + + this.waitress.resolve({address: sender, payload: message, frame: apsFrame} as EmberObject); + super.emit('incomingMessage', { messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, senderEui64: eui64 @@ -191,7 +206,7 @@ export class Driver extends EventEmitter { ieee = new EmberEUI64(ieee); } this.eui64ToNodeId.set(ieee.toString(), nwk); - this.emit('deviceJoined', [nwk, ieee]) + this.emit('deviceJoined', [nwk, ieee]); } private handleReply(sender: number, apsFrame: EmberApsFrame, tsn: number, commandId: number, ...args: any[]) { @@ -214,24 +229,9 @@ export class Driver extends EventEmitter { } public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { - - let seq = apsFrame.sequence+1; - console.assert(!this.pending.has(seq)); - let sendDeferred = new Deferred(); - let replyDeferred = new Deferred(); - this.pending.set(seq, [sendDeferred, replyDeferred]); - - let handle; - let eui64: EmberEUI64; try { - - if (timeout > 0) { - handle = setTimeout(() => { - throw new Error('Timeout while waiting for reply'); - }, timeout); - } - - + let seq = apsFrame.sequence+1; + let eui64: EmberEUI64; if (typeof nwk !== 'number') { eui64 = nwk as EmberEUI64; let strEui64 = eui64.toString(); @@ -248,30 +248,74 @@ export class Driver extends EventEmitter { } else { eui64 = await this.networkIdToEUI64(nwk); } - //await this._ezsp.execCommand('setExtendedTimeout', eui64, true); + await this._ezsp.execCommand('setExtendedTimeout', eui64, true); let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); console.log('unicast message sent, waiting for reply'); - if (v[0] != 0) { - this.pending.delete(seq); - sendDeferred.reject(false); - replyDeferred.reject(false); - throw new Error(`Message send failure ${v[0]}`) - } - - await sendDeferred.promise; - if (timeout > 0) { - await replyDeferred.promise; - } else { - this.pending.delete(seq); - } return true; } catch (e) { return false; - } finally { - if (handle) - clearTimeout(handle); } + // let seq = apsFrame.sequence+1; + + // console.assert(!this.pending.has(seq)); + + + // let sendDeferred = new Deferred(); + // let replyDeferred = new Deferred(); + // this.pending.set(seq, [sendDeferred, replyDeferred]); + + // let handle; + // let eui64: EmberEUI64; + // try { + + // if (timeout > 0) { + // handle = setTimeout(() => { + // throw new Error('Timeout while waiting for reply'); + // }, timeout); + // } + + + // if (typeof nwk !== 'number') { + // eui64 = nwk as EmberEUI64; + // let strEui64 = eui64.toString(); + // let nodeId = this.eui64ToNodeId.get(strEui64); + // if (nodeId === undefined) { + // nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64); + // if (nodeId && nodeId !== 0xFFFF) { + // this.eui64ToNodeId.set(strEui64, nodeId); + // } else { + // throw new Error('Unknown EUI64:' + strEui64); + // } + // } + // nwk = nodeId; + // } else { + // eui64 = await this.networkIdToEUI64(nwk); + // } + // //await this._ezsp.execCommand('setExtendedTimeout', eui64, true); + + // let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + // console.log('unicast message sent, waiting for reply'); + // if (v[0] != 0) { + // this.pending.delete(seq); + // sendDeferred.reject(false); + // replyDeferred.reject(false); + // throw new Error(`Message send failure ${v[0]}`) + // } + + // await sendDeferred.promise; + // if (timeout > 0) { + // await replyDeferred.promise; + // } else { + // this.pending.delete(seq); + // } + // return true; + // } catch (e) { + // return false; + // } finally { + // if (handle) + // clearTimeout(handle); + // } } private handleFrameFailure(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { @@ -341,6 +385,10 @@ export class Driver extends EventEmitter { return this._ezsp.make_zdo_frame(name, ...args); } + public parse_frame_payload(name: string, obj: Buffer) { + return this._ezsp.parse_frame_payload(name, obj); + } + public async addEndpoint({endpoint=1, profileId=260, deviceId=0xBEEF, appFlags=0, inputClusters=[], outputClusters=[]}: AddEndpointParameters) { const res = await this._ezsp.execCommand('addEndpoint', endpoint, @@ -355,10 +403,18 @@ export class Driver extends EventEmitter { this.logger("Ezsp adding endpoint: %s", res); } - // public waitFor( - // type: Type, subsystem: Subsystem, command: string, payload: ZpiObjectPayload = {}, - // timeout: number = timeouts.default - // ): {start: () => {promise: Promise; ID: number}; ID: number} { - // return this.waitress.waitFor({type, subsystem, command, payload}, timeout); - // } + public waitFor(address: number, clusterId: number, timeout: number = 30000) + : {start: () => {promise: Promise; ID: number}; ID: number} { + return this.waitress.waitFor({address, clusterId}, timeout); + } + + private waitressTimeoutFormatter(matcher: EmberWaitressMatcher, timeout: number): string { + return `${matcher} after ${timeout}ms`; + } + + private waitressValidator(payload: EmberObject, matcher: EmberWaitressMatcher): boolean { + const transactionSequenceNumber = payload.frame.sequence; + return (!matcher.address || payload.address === matcher.address) && + payload.frame.clusterId === matcher.clusterId; + } } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 5beccdd6c0..374e092157 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -186,9 +186,9 @@ export class Ezsp extends EventEmitter { private _command(name: string, ...args: any[]): Promise { var c, data, deferred; - this.logger(`Send command ${name}: (${args})`); + this.logger(`---> Send command ${name}: (${args})`); data = this._ezsp_frame(name, ...args); - this.logger(`Send data ${name}: (${data.toString('hex')})`); + this.logger(`---> Send data ${name}: (${data.toString('hex')})`); this._gw.data(data); c = (COMMANDS)[name]; deferred = new Deferred(); @@ -249,21 +249,21 @@ export class Ezsp extends EventEmitter { let cmd = this.COMMANDS_BY_ID.get(frame_id); if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); let frameName = cmd.name; - this.logger("Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); + this.logger("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); if (this._awaiting.has(sequence)) { let entry = this._awaiting.get(sequence); this._awaiting.delete(sequence); if (entry) { console.assert(entry.expectedId === frame_id); [result, data] = t.deserialize(data, entry.schema); - this.logger(`Application frame ${frame_id} (${frameName}) parsed: ${result}`); + this.logger(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); entry.deferred.resolve(result); } } else { schema = cmd.outArgs; frameName = cmd.name; [result, data] = t.deserialize(data, schema); - this.logger(`Application frame ${frame_id} (${frameName}): ${result}`); + this.logger(`<=== Application frame ${frame_id} (${frameName}): ${result}`); super.emit('frame', frameName, ...result); } if ((frame_id === 0)) { @@ -271,6 +271,15 @@ export class Ezsp extends EventEmitter { } } + public parse_frame_payload(name: string, data: Buffer) { + if (Object.keys(ZDO_COMMANDS).indexOf(name) < 0) { + throw new Error('Unknown ZDO command: ' + name); + } + const c = (ZDO_COMMANDS)[name]; + const result = t.deserialize(data, c[1])[0]; + return result; + } + public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); } diff --git a/src/adapter/ezsp/driver/types/emberObj.ts b/src/adapter/ezsp/driver/types/emberObj.ts new file mode 100644 index 0000000000..6deebf9206 --- /dev/null +++ b/src/adapter/ezsp/driver/types/emberObj.ts @@ -0,0 +1,30 @@ +import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './struct'; + + +export class EmberObject { + private readonly _address: number; + private readonly _payload: Buffer; + private readonly _frame: EmberApsFrame; + + private constructor( + address: number, + frame: EmberApsFrame, + payload: Buffer + ) { + this._address = address; + this._payload = payload; + this._frame = frame; + } + + get address(): number { + return this._address; + } + + get frame(): EmberApsFrame { + return this._frame; + } + + get payload(): Buffer { + return this._payload; + } +} diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 172123e568..ee785dcbb7 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -58,7 +58,7 @@ export class UartProtocol implements AsyncIterable { } data_received(data: Buffer) { - //console.log('data_received', data.toString('hex')) + this.logger(`<===== data : ${data.toString('hex')}`); /* Callback when there is data received from the uart */ var frame; if (data.indexOf(CANCEL) >= 0) { @@ -96,6 +96,7 @@ export class UartProtocol implements AsyncIterable { } frame_received(data: Buffer) { + this.logger(`<===== frame : ${data.toString('hex')}`); /* Frame receive handler */ if (((data[0] & 128) === 0)) { this.data_frame_received(data); @@ -127,7 +128,7 @@ export class UartProtocol implements AsyncIterable { data_frame_received(data: Buffer) { /* Data frame receive handler */ var seq; - this.logger("Data frame: %s", data.toString('hex')); + this.logger("<===== Received:", data.toString('hex')); seq = ((data[0] & 112) >> 4); this._rec_seq = ((seq + 1) % 8); this.write(this._ack_frame()); @@ -186,7 +187,7 @@ export class UartProtocol implements AsyncIterable { write(data: Buffer) { /* Send data to the uart */ - this.logger("Sending:", data.toString('hex')); + this.logger("-----> Sending :", data.toString('hex')); return this.writeCb(data); } From 21fee685e33cd71ecb943805b63a8e9634a35e77 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 28 Jan 2021 20:13:34 +0300 Subject: [PATCH 18/63] temporary block nodeDescriptor request --- src/adapter/ezsp/adapter/ezspAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 3f7af712b3..e81deae663 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -79,7 +79,7 @@ class EZSPAdapter extends Adapter { }; if (nwk == 0) { - const nd = this.nodeDescriptor(nwk); + //const nd = this.nodeDescriptor(nwk); } else { this.emit(Events.Events.deviceJoined, payload); } From 461452da36a4899b57a863a2b623a02de028e378 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 29 Jan 2021 18:42:28 +0300 Subject: [PATCH 19/63] starting rewrite uart communication --- src/adapter/ezsp/adapter/ezspAdapter.ts | 632 +++++++------- src/adapter/ezsp/driver/ezsp.ts | 920 ++++++++++++++------- src/adapter/ezsp/driver/uart.ts | 1004 +++++++++++++---------- 3 files changed, 1504 insertions(+), 1052 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index e81deae663..890b6ed7dd 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -1,317 +1,317 @@ -/* istanbul ignore file */ -/* eslint-disable */ -import { - NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, - DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, - StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, -} from '../../tstype'; -import Debug from "debug"; -import Adapter from '../../adapter'; -const debug = Debug("zigbee-herdsman:ezsp:adapter"); -import {Ezsp, Driver} from '../driver'; -import { EmberApsFrame } from '../driver/types/struct'; -import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; -import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; -import * as Events from '../../events'; -import * as Zcl from '../../../zcl'; -import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; -import {Queue, Waitress, Wait} from '../../../utils'; - - -interface WaitressMatcher { - address: number | string; - endpoint: number; - transactionSequenceNumber?: number; - frameType: FrameType; - clusterID: number; - commandIdentifier: number; - direction: number; -} - -class EZSPAdapter extends Adapter { - private driver: Driver; - private port: SerialPortOptions; - private transactionID: number; - - public constructor(networkOptions: NetworkOptions, - serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { - super(networkOptions, serialPortOptions, backupPath, adapterOptions); - this.transactionID = 1; - this.port = serialPortOptions; - this.driver = new Driver(); - this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); - this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); - this.driver.on('incomingMessage', this.processMessage.bind(this)); - } - - private nextTransactionID(): number { - this.transactionID++; - - if (this.transactionID > 255) { - this.transactionID = 1; - } - - return this.transactionID; - } - - private async processMessage(frame: any) { - // todo - debug(`processMessage: ${JSON.stringify(frame)}`); - if (!frame.senderEui64) { - frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) - } - if (frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { - const [nwk, rest] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); - const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); - debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); - this.handleDeviceJoin([nwk, ieee, 0]); - } - this.emit('event', frame); - } - - private handleDeviceJoin(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device join request received: %s %s', nwk, ieee); - const payload: Events.DeviceJoinedPayload = { - networkAddress: nwk, - ieeeAddr: ieee, - }; - - if (nwk == 0) { - //const nd = this.nodeDescriptor(nwk); - } else { - this.emit(Events.Events.deviceJoined, payload); - } - } - - private handleDeviceLeft(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device left network request received: %s %s', nwk, ieee); - // let devices = this.getDevices(); - - // let idx = devices.findIndex(d => d.nodeId === nwk && d.eui64 === ieee.toString()); - // if (idx >= 0) { - // devices = devices.splice(idx, 1); - // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); - // } - } - - /** - * Adapter methods - */ - public async start(): Promise { - await this.driver.startup(this.port.path, { - baudRate: this.port.baudRate || 115200, - parity: 'none', - stopBits: 1, - xon: true, - xoff: true - }, this.networkOptions, debug); - return Promise.resolve("resumed"); - } - - public async stop(): Promise { - await this.driver.stop(); - } - - public static async isValidPath(path: string): Promise { - // todo - return true; - } - - public static async autoDetectPath(): Promise { - // todo - return ''; - } - - public async getCoordinator(): Promise { - // todo - return { - networkAddress: 0x0000, - manufacturerID: 0x1135, - ieeeAddr: '', - endpoints: [], - }; - } - - public async permitJoin(seconds: number, networkAddress: number): Promise { - // todo - await this.driver.permitJoining(seconds); - return Promise.resolve(); - } - - public async getCoordinatorVersion(): Promise { - // todo - return {type: '', meta: {}}; - } - - public async reset(type: 'soft' | 'hard'): Promise { - return Promise.reject(); - } - - public async supportsLED(): Promise { - return false; - } - - public async setLED(enabled: boolean): Promise { - return Promise.reject(); - } - - public async lqi(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async routingTable(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async nodeDescriptor(networkAddress: number): Promise { - // todo - try { - debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); - const result = await this.nodeDescriptorInternal(networkAddress); - return result; - } catch (error) { - debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); - throw error; - } - } - - private async nodeDescriptorInternal(networkAddress: number): Promise { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Node_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); - const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - await this.driver.request(networkAddress, frame, payload); - const descriptor = await response.start().promise; - debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); - const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); - debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); - return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; - } - - public async activeEndpoints(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { - // todo - return Promise.reject(); - } - - public waitFor( - networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, - transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {promise: Promise; cancel: () => void} { - // todo - return {cancel: undefined, promise: undefined}; - } - - public async sendZclFrameToEndpoint( - ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, - disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, - ): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { - // todo - return Promise.resolve(); - } - - public async bind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint?: number - ): Promise { - // todo - return Promise.reject(); - } - - public async unbind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint: number - ): Promise { - // todo - return Promise.reject(); - } - - public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { - // todo - return Promise.reject(); - } - - public async getNetworkParameters(): Promise { - return { - panID: this.driver.networkParams.panId, - extendedPanID: this.driver.networkParams.extendedPanId[0], - channel: this.driver.networkParams.radioChannel - }; - } - - public async supportsBackup(): Promise { - //todo - return false; - } - - public async backup(): Promise { - // todo - return Promise.reject(); - } - - public async restoreChannelInterPAN(): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcast( - zclFrame: ZclFrame, timeout: number - ): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcastWithResponse( - zclFrame: ZclFrame, timeout: number - ): Promise { - throw new Error("not supported"); - } - - public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { - throw new Error("not supported"); - } - - public async setTransmitPower(value: number): Promise { - // todo - } - - public async setChannelInterPAN(channel: number): Promise { - //todo - } -} - - +/* istanbul ignore file */ +/* eslint-disable */ +import { + NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, + DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, + StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, +} from '../../tstype'; +import Debug from "debug"; +import Adapter from '../../adapter'; +const debug = Debug("zigbee-herdsman:adapter:ezsp"); +import {Ezsp, Driver} from '../driver'; +import { EmberApsFrame } from '../driver/types/struct'; +import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; +import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; +import * as Events from '../../events'; +import * as Zcl from '../../../zcl'; +import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; +import {Queue, Waitress, Wait} from '../../../utils'; + + +interface WaitressMatcher { + address: number | string; + endpoint: number; + transactionSequenceNumber?: number; + frameType: FrameType; + clusterID: number; + commandIdentifier: number; + direction: number; +} + +class EZSPAdapter extends Adapter { + private driver: Driver; + private port: SerialPortOptions; + private transactionID: number; + + public constructor(networkOptions: NetworkOptions, + serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { + super(networkOptions, serialPortOptions, backupPath, adapterOptions); + this.transactionID = 1; + this.port = serialPortOptions; + this.driver = new Driver(); + this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); + this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); + this.driver.on('incomingMessage', this.processMessage.bind(this)); + } + + private nextTransactionID(): number { + this.transactionID++; + + if (this.transactionID > 255) { + this.transactionID = 1; + } + + return this.transactionID; + } + + private async processMessage(frame: any) { + // todo + debug(`processMessage: ${JSON.stringify(frame)}`); + if (!frame.senderEui64) { + frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) + } + if (frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { + const [nwk, rest] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); + debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); + this.handleDeviceJoin([nwk, ieee, 0]); + } + this.emit('event', frame); + } + + private handleDeviceJoin(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device join request received: %s %s', nwk, ieee); + const payload: Events.DeviceJoinedPayload = { + networkAddress: nwk, + ieeeAddr: ieee, + }; + + if (nwk == 0) { + //const nd = this.nodeDescriptor(nwk); + } else { + this.emit(Events.Events.deviceJoined, payload); + } + } + + private handleDeviceLeft(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device left network request received: %s %s', nwk, ieee); + // let devices = this.getDevices(); + + // let idx = devices.findIndex(d => d.nodeId === nwk && d.eui64 === ieee.toString()); + // if (idx >= 0) { + // devices = devices.splice(idx, 1); + // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); + // } + } + + /** + * Adapter methods + */ + public async start(): Promise { + await this.driver.startup(this.port.path, { + baudRate: this.port.baudRate || 115200, + parity: 'none', + stopBits: 1, + xon: true, + xoff: true + }, this.networkOptions, debug); + return Promise.resolve("resumed"); + } + + public async stop(): Promise { + await this.driver.stop(); + } + + public static async isValidPath(path: string): Promise { + // todo + return true; + } + + public static async autoDetectPath(): Promise { + // todo + return ''; + } + + public async getCoordinator(): Promise { + // todo + return { + networkAddress: 0x0000, + manufacturerID: 0x1135, + ieeeAddr: '', + endpoints: [], + }; + } + + public async permitJoin(seconds: number, networkAddress: number): Promise { + // todo + await this.driver.permitJoining(seconds); + return Promise.resolve(); + } + + public async getCoordinatorVersion(): Promise { + // todo + return {type: '', meta: {}}; + } + + public async reset(type: 'soft' | 'hard'): Promise { + return Promise.reject(); + } + + public async supportsLED(): Promise { + return false; + } + + public async setLED(enabled: boolean): Promise { + return Promise.reject(); + } + + public async lqi(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async routingTable(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async nodeDescriptor(networkAddress: number): Promise { + // todo + try { + debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); + const result = await this.nodeDescriptorInternal(networkAddress); + return result; + } catch (error) { + debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); + throw error; + } + } + + private async nodeDescriptorInternal(networkAddress: number): Promise { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Node_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); + const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + await this.driver.request(networkAddress, frame, payload); + const descriptor = await response.start().promise; + debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); + const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); + debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); + return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + } + + public async activeEndpoints(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { + // todo + return Promise.reject(); + } + + public waitFor( + networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, + transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, + ): {promise: Promise; cancel: () => void} { + // todo + return {cancel: undefined, promise: undefined}; + } + + public async sendZclFrameToEndpoint( + ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, + disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, + ): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { + // todo + return Promise.resolve(); + } + + public async bind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint?: number + ): Promise { + // todo + return Promise.reject(); + } + + public async unbind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint: number + ): Promise { + // todo + return Promise.reject(); + } + + public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { + // todo + return Promise.reject(); + } + + public async getNetworkParameters(): Promise { + return { + panID: this.driver.networkParams.panId, + extendedPanID: this.driver.networkParams.extendedPanId[0], + channel: this.driver.networkParams.radioChannel + }; + } + + public async supportsBackup(): Promise { + //todo + return false; + } + + public async backup(): Promise { + // todo + return Promise.reject(); + } + + public async restoreChannelInterPAN(): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcast( + zclFrame: ZclFrame, timeout: number + ): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcastWithResponse( + zclFrame: ZclFrame, timeout: number + ): Promise { + throw new Error("not supported"); + } + + public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { + throw new Error("not supported"); + } + + public async setTransmitPower(value: number): Promise { + // todo + } + + public async setChannelInterPAN(channel: number): Promise { + //todo + } +} + + export default EZSPAdapter; \ No newline at end of file diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 374e092157..85287237e0 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,286 +1,634 @@ -import * as t from './types'; -import { UartProtocol } from './uart'; -import { COMMANDS, ZDO_COMMANDS } from './commands'; - -import { Deferred } from './utils'; -import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; -import { EventEmitter } from 'events'; -import { EmberApsFrame } from './types/struct'; -import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; - -export class Ezsp extends EventEmitter { - ezsp_version = 4; - _gw: UartProtocol; - _seq = 0; - _tc_policy: any; - _awaiting = new Map }>(); - COMMANDS_BY_ID = new Map(); - _cbCounter = 0; - logger: any; - - constructor(logger: any) { - super(); - this.logger = logger; - for (let name in COMMANDS) { - let details = (COMMANDS)[name]; - this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); - } - } - - async connect(device: string, options: {}) { - console.assert(!this._gw); - this._gw = await UartProtocol.connect(device, options, this.logger); - this.startReadQueue(); - } - - private async startReadQueue() { - for await (let frame of this._gw) { - try { - this.frame_received(frame); - } catch (e) { - this.logger('Error handling frame', e); - } - } - } - - reset() { - return this._gw.reset(); - } - - async version() { - let version = this.ezsp_version; - let result = await this._command("version", version); - if ((result[0] !== version)) { - this.logger("Switching to eszp version %d", result[0]); - await this._command("version", result[0]); - } - return result[0]; - } - - async networkInit() { - let result; - [result] = await this._command("networkInit"); - console.log('network init result', result); - return result === EmberStatus.SUCCESS; - } - - async leaveNetwork() { - var fut: Deferred, v, st; - fut = new Deferred(); - this.on('frame', (frameName: string, response: any) => { - if ((frameName === "stackStatusHandler")) { - fut.resolve(response); - } - }) - v = await this._command("leaveNetwork"); - if ((v[0] !== EmberStatus.SUCCESS)) { - this.logger("Failure to leave network:" + v); - throw new Error(("Failure to leave network:" + v)); - } - v = await fut.promise; - if ((v !== EmberStatus.NETWORK_DOWN)) { - this.logger("Failure to leave network:" + v); - throw new Error(("Failure to leave network:" + v)); - } - return v; - } - - async setConfigurationValue(configId: number, value: any) { - let ret; - [ret] = await this.execCommand('setConfigurationValue', configId, value); - console.assert(ret === EmberStatus.SUCCESS); - this.logger('Set %s = %s', configId, value); - } - - async getConfigurationValue(configId: number) { - let ret, value; - [ret, value] = await this.execCommand('getConfigurationValue', configId); - console.assert(ret === EmberStatus.SUCCESS); - this.logger('Get %s = %s', configId, value); - return value; - } - - async getMulticastTableEntry(index: number) { - let ret, value; - [ret, value] = await this.execCommand('getMulticastTableEntry', index); - console.assert(ret === EmberStatus.SUCCESS); - return [ret, value]; - } - - async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry) { - let ret; - [ret] = await this.execCommand('setMulticastTableEntry', index, entry); - console.assert(ret === EmberStatus.SUCCESS); - return [ret]; - } - - async setInitialSecurityState(entry: t.EmberInitialSecurityState) { - let ret; - [ret] = await this.execCommand('setInitialSecurityState', entry); - console.assert(ret === EmberStatus.SUCCESS); - return [ret]; - } - - async getCurrentSecurityState(){ - let ret, res; - [ret, res] = await this.execCommand('getCurrentSecurityState'); - console.assert(ret === EmberStatus.SUCCESS); - return [ret, res]; - } - - async setValue(valueId: t.EzspValueId, value: any) { - let ret; - [ret] = await this.execCommand('setValue', valueId, value); - console.assert(ret === EmberStatus.SUCCESS); - return [ret]; - } - - async getValue(valueId: t.EzspValueId) { - let ret, value; - [ret, value] = await this.execCommand('getValue', valueId); - console.assert(ret === EmberStatus.SUCCESS); - return value; - } - - async updatePolicies() { - // Set up the policies for what the NCP should do. - const policies = [ - [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], - [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], - [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], - ]; - - for (let [policy, value] of policies) { - const [status] = await this.execCommand('setPolicy', policy, value); - console.assert(status == EmberStatus.SUCCESS); - } - } - - close() { - return this._gw.close(); - } - - public make_zdo_frame(name: string, ...args: any[]): Buffer { - var c, data, frame, cmd_id; - c = (ZDO_COMMANDS)[name]; - data = t.serialize(args, c[1]); - return data; - } - private _ezsp_frame(name: string, ...args: any[]) { - var c, data, frame, cmd_id; - c = (COMMANDS)[name]; - data = t.serialize(args, c[1]); - frame = [(this._seq & 255)]; - if ((this.ezsp_version < 8)) { - if ((this.ezsp_version >= 5)) { - frame.push(0x00, 0xFF, 0x00, c[0]); - } else { - frame.push(0x00, c[0]); - } - } else { - cmd_id = t.serialize([c[0]], [t.uint16_t]); - frame.push(0x00, 0x01, ...cmd_id); - } - return Buffer.concat([Buffer.from(frame), data]); - } - - private _command(name: string, ...args: any[]): Promise { - var c, data, deferred; - this.logger(`---> Send command ${name}: (${args})`); - data = this._ezsp_frame(name, ...args); - this.logger(`---> Send data ${name}: (${data.toString('hex')})`); - this._gw.data(data); - c = (COMMANDS)[name]; - deferred = new Deferred(); - this._awaiting.set(this._seq, { expectedId: c[0], schema: c[2], deferred }); - this._seq = (this._seq + 1 % 256); - return deferred.promise; - } - - async formNetwork(parameters: {}) { - var fut: Deferred, v, st; - fut = new Deferred(); - this.on('frame', (frameName: string, response: any) => { - if ((frameName === "stackStatusHandler")) { - fut.resolve(response); - } - }) - v = await this._command("formNetwork", parameters); - if ((v[0] !== EmberStatus.SUCCESS)) { - this.logger("Failure forming network:" + v); - throw new Error(("Failure forming network:" + v)); - } - v = await fut.promise; - if ((v !== EmberStatus.NETWORK_UP)) { - this.logger("Failure forming network:" + v); - throw new Error(("Failure forming network:" + v)); - } - return v; - } - - execCommand(name: string, ...args: any[]): any { - if (Object.keys(COMMANDS).indexOf(name) < 0) { - throw new Error('Unknown command: ' + name); - } - return this._command(name, ...args); - } - - frame_received(data: Buffer) { - /*Handle a received EZSP frame - - The protocol has taken care of UART specific framing etc, so we should - just have EZSP application stuff here, with all escaping/stuffing and - data randomization removed. - */ - var frame_id: number, result, schema, sequence; - if ((this.ezsp_version < 8)) { - [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; - } else { - sequence = data[0]; - [[frame_id], data] = t.deserialize(data.slice(3), [t.uint16_t]); - } - if ((frame_id === 255)) { - frame_id = 0; - if ((data.length > 1)) { - frame_id = data[1]; - data = data.slice(2); - } - } - let cmd = this.COMMANDS_BY_ID.get(frame_id); - if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); - let frameName = cmd.name; - this.logger("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); - if (this._awaiting.has(sequence)) { - let entry = this._awaiting.get(sequence); - this._awaiting.delete(sequence); - if (entry) { - console.assert(entry.expectedId === frame_id); - [result, data] = t.deserialize(data, entry.schema); - this.logger(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); - entry.deferred.resolve(result); - } - } else { - schema = cmd.outArgs; - frameName = cmd.name; - [result, data] = t.deserialize(data, schema); - this.logger(`<=== Application frame ${frame_id} (${frameName}): ${result}`); - super.emit('frame', frameName, ...result); - } - if ((frame_id === 0)) { - this.ezsp_version = result[0]; - } - } - - public parse_frame_payload(name: string, data: Buffer) { - if (Object.keys(ZDO_COMMANDS).indexOf(name) < 0) { - throw new Error('Unknown ZDO command: ' + name); - } - const c = (ZDO_COMMANDS)[name]; - const result = t.deserialize(data, c[1])[0]; - return result; - } - - public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { - return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); - } -} +import * as t from './types'; +//import { UartProtocol } from './uart'; +import { Writer, Parser } from './uart'; +import { COMMANDS, ZDO_COMMANDS } from './commands'; + +import { Deferred, crc16ccitt } from './utils'; +import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; +import { EventEmitter } from 'events'; +import { EmberApsFrame } from './types/struct'; +import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; +import SerialPort from 'serialport'; +import net from 'net'; + +import SerialPortUtils from '../../serialPortUtils'; +import SocketPortUtils from '../../socketPortUtils'; +import Debug from "debug"; + +const debug = { + error: Debug('zigbee-herdsman:adapter:ezsp:error'), + log: Debug('zigbee-herdsman:adapter:ezsp:log'), +}; + +const FLAG = 0x7E // Marks end of frame +const ESCAPE = 0x7D +const XON = 0x11 // Resume transmission +const XOFF = 0x13 // Stop transmission +const SUBSTITUTE = 0x18 +const CANCEL = 0x1A // Terminates a frame in progress +const STUFF = 0x20 +const RANDOMIZE_START = 0x42 +const RANDOMIZE_SEQ = 0xB8 +const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] + +enum NcpResetCode { + RESET_UNKNOWN_REASON = 0x00, + RESET_EXTERNAL = 0x01, + RESET_POWER_ON = 0x02, + RESET_WATCHDOG = 0x03, + RESET_ASSERT = 0x06, + RESET_BOOTLOADER = 0x09, + RESET_SOFTWARE = 0x0B, + ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, + ERROR_UNKNOWN_EM3XX_ERROR = 0x80, +} + + +export class Ezsp extends EventEmitter { + ezsp_version = 4; + //_gw: UartProtocol; + _seq = 0; + send_seq = 0; + recv_seq = 0; + _tc_policy: any; + _awaiting = new Map }>(); + COMMANDS_BY_ID = new Map(); + _cbCounter = 0; + reset_deferred: Deferred; + logger: any; + private portType: 'serial' | 'socket'; + private serialPort: SerialPort; + private socketPort: net.Socket; + private writer: Writer; + private parser: Parser; + private initialized: boolean; + + constructor(logger: any) { + super(); + this.initialized = false; + this.logger = logger; + for (let name in COMMANDS) { + let details = (COMMANDS)[name]; + this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); + } + this.onParsed = this.onParsed.bind(this); + this.onPortClose = this.onPortClose.bind(this); + } + + private onParsed(data: Buffer): void { + try { + // const object = ZpiObject.fromUnpiFrame(frame); + // const message = + // `<-- ${Subsystem[object.subsystem]} - ${object.command} - ${JSON.stringify(object.payload)}`; + // this.log(object.type, message); + // this.waitress.resolve(object); + // this.emit('received', object); + debug.log(`<===== frame : ${data.toString('hex')}`); + /* Frame receive handler */ + switch (true) { + case ((data[0] & 128) === 0): + debug.log("DATA frame: %s", data.toString('hex')); + this.data_frame_received(data); + break; + + case ((data[0] & 224) === 128): + debug.log("ACK frame: %s", data.toString('hex')); + this.handle_ack(data[0]); + break; + + case ((data[0] & 224) === 160): + debug.log("NAK frame: %s", data.toString('hex')); + this.handle_nak(data[0]); + break; + + case (data[0] === 192): + debug.log("RST frame: %s", data.toString('hex')); + break; + + case (data[0] === 193): + debug.log("RSTACK frame: %s", data.toString('hex')); + this.rstack_frame_received(data); + break; + + case (data[0] === 194): + debug.log("Error frame:", data.toString('hex')); + break; + default: + debug.error("UNKNOWN FRAME RECEIVED: %r", data); + } + + } catch (error) { + debug.error(`Error while parsing to ZpiObject '${error.stack}'`); + } + } + + private data_frame_received(data: Buffer) { + /* Data frame receive handler */ + var seq; + seq = ((data[0] & 112) >> 4); + this.recv_seq = ((seq + 1) % 8); + this.writer.writeBuffer(this.make_ack_frame()); + this.handle_ack(data[0]); + data = data.slice(1, (- 3)); + debug.log(data); + //this.waitress.resolve(data); + this.emit('received', this.randomize(data)); + } + + private handle_ack(control: number) { + /* Handle an acknowledgement frame */ + // var ack, pending; + // ack = (((control & 7) - 1) % 8); + // if ((ack === this._pending[0])) { + // [pending, this._pending] = [this._pending, [(- 1), null]]; + // pending[1].set_result(true); + // } + } + + private handle_nak(control: number) { + /* Handle negative acknowledgment frame */ + // let nak = (control & 7); + // if ((nak === this._pending[0])) { + // this._pending[1].set_result(false); + // } + } + + private rstack_frame_received(data: Buffer) { + /* Reset acknowledgement frame receive handler */ + var code; + this.send_seq = 0; + this.recv_seq = 0; + try { + code = NcpResetCode[data[2]]; + } catch (e) { + code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; + } + this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { + return; + } + if ((!this.reset_deferred)) { + this.logger("Reset future is None"); + return; + } + this.reset_deferred.resolve(true); + } + + private make_ack_frame(): Buffer { + /* Construct a acknowledgement frame */ + console.assert(((0 <= this.recv_seq) && (this.recv_seq < 8))); + return this.make_frame([(0b10000000 | (this.recv_seq & 0b00000111))]); + } + + private make_frame(control: ArrayLike, data?: ArrayLike): Buffer { + /* Construct a frame */ + const ctrlArr: Array = Array.from(control); + const dataArr: Array = (data && Array.from(data)) || []; + + const sum = ctrlArr.concat(dataArr); + + let crc = crc16ccitt(Buffer.from(sum), 65535); + let crcArr = [(crc >> 8), (crc % 256)]; + return Buffer.concat([this.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); + } + + private randomize(s: Buffer): Buffer { + /*XOR s with a pseudo-random sequence for transmission + Used only in data frames + */ + let rand = RANDOMIZE_START; + let out = Buffer.alloc(s.length); + let outIdx = 0; + for (let c of s){ + out.writeUInt8(c ^ rand, outIdx++); + if ((rand % 2)) { + rand = ((rand >> 1) ^ RANDOMIZE_SEQ); + } else { + rand = (rand >> 1); + } + } + return out; + } + + private stuff(s: Iterable): Buffer { + /* Byte stuff (escape) a string for transmission */ + let out = Buffer.alloc(256); + let outIdx = 0; + for (const c of s) { + if (RESERVED.includes(c)) { + out.writeUInt8(ESCAPE, outIdx++); + out.writeUInt8(c ^ STUFF, outIdx++); + } else { + out.writeUInt8(c, outIdx++); + } + } + return out.slice(0, outIdx); + } + + public isInitialized(): boolean { + return this.initialized; + } + + private onPortClose(): void { + debug.log('Port closed'); + this.initialized = false; + this.emit('close'); + } + + async connect(path: string, options: {}) { + //console.assert(!this._gw); + this.portType = SocketPortUtils.isTcpPath(path) ? 'socket' : 'serial'; + //this._gw = await UartProtocol.connect(device, options, this.logger); + //this.startReadQueue(); + this.portType === 'serial' ? await this.openSerialPort(path, options) : await this.openSocketPort(path, options); + } + + private async openSerialPort(path: string, opt: {}): Promise { + // @ts-ignore + const options = {baudRate: opt.baudRate, rtscts: false, autoOpen: false}; + + debug.log(`Opening SerialPort with ${path} and ${JSON.stringify(options)}`); + this.serialPort = new SerialPort(path, options); + + this.writer = new Writer(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.writer.pipe(this.serialPort); + + this.parser = new Parser(); + this.serialPort.pipe(this.parser); + this.parser.on('parsed', this.onParsed); + + return new Promise((resolve, reject): void => { + this.serialPort.open(async (error): Promise => { + if (error) { + reject(new Error(`Error while opening serialport '${error}'`)); + this.initialized = false; + if (this.serialPort.isOpen) { + this.serialPort.close(); + } + } else { + debug.log('Serialport opened'); + this.serialPort.once('close', this.onPortClose); + this.serialPort.once('error', (error) => { + debug.error(`Serialport error: ${error}`); + }); + // reset + await this.reset(); + this.initialized = true; + resolve(); + } + }); + }); + } + + private async openSocketPort(path: string, options: {}): Promise { + const info = SocketPortUtils.parseTcpPath(path); + debug.log(`Opening TCP socket with ${info.host}:${info.port}`); + + this.socketPort = new net.Socket(); + this.socketPort.setNoDelay(true); + this.socketPort.setKeepAlive(true, 15000); + + this.writer = new Writer(); + this.writer.pipe(this.socketPort); + + this.parser = new Parser(); + this.socketPort.pipe(this.parser); + this.parser.on('parsed', this.onParsed); + + return new Promise((resolve, reject): void => { + this.socketPort.on('connect', function() { + debug.log('Socket connected'); + }); + + // eslint-disable-next-line + const self = this; + this.socketPort.on('ready', async function() { + debug.log('Socket ready'); + // reset + await this.reset(); + self.initialized = true; + resolve(); + }); + + this.socketPort.once('close', this.onPortClose); + + this.socketPort.on('error', function () { + debug.log('Socket error'); + reject(new Error(`Error while opening socket`)); + self.initialized = false; + }); + + this.socketPort.connect(info.port, info.host); + }); + } + + private write(data: Buffer) { + debug.log("write data: %s", data.toString('hex')); + let seq = this.send_seq; + this.send_seq = ((seq + 1) % 8); + let pack; + try { + pack = this.data_frame(data, seq, 0); + this.writer.writeBuffer(pack); + } catch (e) { + pack = this.data_frame(data, seq, 1); + this.writer.writeBuffer(pack); + } + } + + private data_frame(data: Buffer, seq: number, rxmit: number) { + /* Construct a data frame */ + let control; + console.assert(((0 <= seq) && (seq <= 7))); + console.assert(((0 <= rxmit) && (rxmit <= 1))); + control = (((seq << 4) | (rxmit << 3)) | this.recv_seq); + return this.make_frame([control], this.randomize(data)); + } + + // private async startReadQueue() { + // for await (let frame of this._gw) { + // try { + // this.frame_received(frame); + // } catch (e) { + // this.logger('Error handling frame', e); + // } + // } + // } + + async reset() { + // return this._gw.reset(); + debug.log('uart reseting'); + if ((this.reset_deferred)) { + throw new TypeError("reset can only be called on a new connection"); + } + /* Construct a reset frame */ + const rst_frame = Buffer.concat([Buffer.from([CANCEL]), this.make_frame([0xC0])]); + //this.write(rst_frame); + this.writer.writeBuffer(rst_frame); + this.reset_deferred = new Deferred(); + return this.reset_deferred.promise; + } + + async version() { + let version = this.ezsp_version; + let result = await this._command("version", version); + if ((result[0] !== version)) { + this.logger("Switching to eszp version %d", result[0]); + await this._command("version", result[0]); + } + return result[0]; + } + + async networkInit() { + let result; + [result] = await this._command("networkInit"); + console.log('network init result', result); + return result === EmberStatus.SUCCESS; + } + + async leaveNetwork() { + var fut: Deferred, v, st; + fut = new Deferred(); + this.on('frame', (frameName: string, response: any) => { + if ((frameName === "stackStatusHandler")) { + fut.resolve(response); + } + }) + v = await this._command("leaveNetwork"); + if ((v[0] !== EmberStatus.SUCCESS)) { + this.logger("Failure to leave network:" + v); + throw new Error(("Failure to leave network:" + v)); + } + v = await fut.promise; + if ((v !== EmberStatus.NETWORK_DOWN)) { + this.logger("Failure to leave network:" + v); + throw new Error(("Failure to leave network:" + v)); + } + return v; + } + + async setConfigurationValue(configId: number, value: any) { + let ret; + [ret] = await this.execCommand('setConfigurationValue', configId, value); + console.assert(ret === EmberStatus.SUCCESS); + this.logger('Set %s = %s', configId, value); + } + + async getConfigurationValue(configId: number) { + let ret, value; + [ret, value] = await this.execCommand('getConfigurationValue', configId); + console.assert(ret === EmberStatus.SUCCESS); + this.logger('Get %s = %s', configId, value); + return value; + } + + async getMulticastTableEntry(index: number) { + let ret, value; + [ret, value] = await this.execCommand('getMulticastTableEntry', index); + console.assert(ret === EmberStatus.SUCCESS); + return [ret, value]; + } + + async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry) { + let ret; + [ret] = await this.execCommand('setMulticastTableEntry', index, entry); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + + async setInitialSecurityState(entry: t.EmberInitialSecurityState) { + let ret; + [ret] = await this.execCommand('setInitialSecurityState', entry); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + + async getCurrentSecurityState(){ + let ret, res; + [ret, res] = await this.execCommand('getCurrentSecurityState'); + console.assert(ret === EmberStatus.SUCCESS); + return [ret, res]; + } + + async setValue(valueId: t.EzspValueId, value: any) { + let ret; + [ret] = await this.execCommand('setValue', valueId, value); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + + async getValue(valueId: t.EzspValueId) { + let ret, value; + [ret, value] = await this.execCommand('getValue', valueId); + console.assert(ret === EmberStatus.SUCCESS); + return value; + } + + async updatePolicies() { + // Set up the policies for what the NCP should do. + const policies = [ + [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], + [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], + [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], + ]; + + for (let [policy, value] of policies) { + const [status] = await this.execCommand('setPolicy', policy, value); + console.assert(status == EmberStatus.SUCCESS); + } + } + + // close() { + // return this._gw.close(); + // } + public close(): Promise { + return new Promise((resolve, reject): void => { + if (this.initialized) { + if (this.portType === 'serial') { + this.serialPort.flush((): void => { + this.serialPort.close((error): void => { + this.initialized = false; + error == null ? + resolve() : + reject(new Error(`Error while closing serialport '${error}'`)); + this.emit('close'); + }); + }); + } else { + this.socketPort.destroy(); + resolve(); + } + } else { + resolve(); + this.emit('close'); + } + }); + } + + public make_zdo_frame(name: string, ...args: any[]): Buffer { + var c, data, frame, cmd_id; + c = (ZDO_COMMANDS)[name]; + data = t.serialize(args, c[1]); + return data; + } + private _ezsp_frame(name: string, ...args: any[]) { + var c, data, frame, cmd_id; + c = (COMMANDS)[name]; + data = t.serialize(args, c[1]); + frame = [(this._seq & 255)]; + if ((this.ezsp_version < 8)) { + if ((this.ezsp_version >= 5)) { + frame.push(0x00, 0xFF, 0x00, c[0]); + } else { + frame.push(0x00, c[0]); + } + } else { + cmd_id = t.serialize([c[0]], [t.uint16_t]); + frame.push(0x00, 0x01, ...cmd_id); + } + return Buffer.concat([Buffer.from(frame), data]); + } + + private _command(name: string, ...args: any[]): Promise { + var c, data, deferred; + debug.log(`---> Send command ${name}: (${args})`); + data = this._ezsp_frame(name, ...args); + debug.log(`---> Send data ${name}: (${data.toString('hex')})`); + //this._gw.data(data); + this.write(data); + c = (COMMANDS)[name]; + deferred = new Deferred(); + this._awaiting.set(this._seq, { expectedId: c[0], schema: c[2], deferred }); + this._seq = (this._seq + 1 % 256); + return deferred.promise; + } + + async formNetwork(parameters: {}) { + var fut: Deferred, v, st; + fut = new Deferred(); + this.on('frame', (frameName: string, response: any) => { + if ((frameName === "stackStatusHandler")) { + fut.resolve(response); + } + }) + v = await this._command("formNetwork", parameters); + if ((v[0] !== EmberStatus.SUCCESS)) { + this.logger("Failure forming network:" + v); + throw new Error(("Failure forming network:" + v)); + } + v = await fut.promise; + if ((v !== EmberStatus.NETWORK_UP)) { + this.logger("Failure forming network:" + v); + throw new Error(("Failure forming network:" + v)); + } + return v; + } + + execCommand(name: string, ...args: any[]): any { + if (Object.keys(COMMANDS).indexOf(name) < 0) { + throw new Error('Unknown command: ' + name); + } + return this._command(name, ...args); + } + + frame_received(data: Buffer) { + /*Handle a received EZSP frame + + The protocol has taken care of UART specific framing etc, so we should + just have EZSP application stuff here, with all escaping/stuffing and + data randomization removed. + */ + var frame_id: number, result, schema, sequence; + if ((this.ezsp_version < 8)) { + [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; + } else { + sequence = data[0]; + [[frame_id], data] = t.deserialize(data.slice(3), [t.uint16_t]); + } + if ((frame_id === 255)) { + frame_id = 0; + if ((data.length > 1)) { + frame_id = data[1]; + data = data.slice(2); + } + } + let cmd = this.COMMANDS_BY_ID.get(frame_id); + if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); + let frameName = cmd.name; + this.logger("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); + if (this._awaiting.has(sequence)) { + let entry = this._awaiting.get(sequence); + this._awaiting.delete(sequence); + if (entry) { + console.assert(entry.expectedId === frame_id); + [result, data] = t.deserialize(data, entry.schema); + this.logger(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); + entry.deferred.resolve(result); + } + } else { + schema = cmd.outArgs; + frameName = cmd.name; + [result, data] = t.deserialize(data, schema); + this.logger(`<=== Application frame ${frame_id} (${frameName}): ${result}`); + super.emit('frame', frameName, ...result); + } + if ((frame_id === 0)) { + this.ezsp_version = result[0]; + } + } + + public parse_frame_payload(name: string, data: Buffer) { + if (Object.keys(ZDO_COMMANDS).indexOf(name) < 0) { + throw new Error('Unknown ZDO command: ' + name); + } + const c = (ZDO_COMMANDS)[name]; + const result = t.deserialize(data, c[1])[0]; + return result; + } + + public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { + return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); + } +} diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index ee785dcbb7..eabca5b572 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -1,450 +1,554 @@ -import SerialPort from 'serialport'; -import net from "net"; -import { Deferred, AsyncQueue, crc16ccitt } from './utils'; -import SerialPortUtils from "../../serialPortUtils"; -import SocketPortUtils from "../../socketPortUtils"; - -const FLAG = 0x7E // Marks end of frame -const ESCAPE = 0x7D -const XON = 0x11 // Resume transmission -const XOFF = 0x13 // Stop transmission -const SUBSTITUTE = 0x18 -const CANCEL = 0x1A // Terminates a frame in progress -const STUFF = 0x20 -const RANDOMIZE_START = 0x42 -const RANDOMIZE_SEQ = 0xB8 -const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] - -class Terminator { -} - -enum NcpResetCode { - RESET_UNKNOWN_REASON = 0x00, - RESET_EXTERNAL = 0x01, - RESET_POWER_ON = 0x02, - RESET_WATCHDOG = 0x03, - RESET_ASSERT = 0x06, - RESET_BOOTLOADER = 0x09, - RESET_SOFTWARE = 0x0B, - ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, - ERROR_UNKNOWN_EM3XX_ERROR = 0x80, -} - -export class UartProtocol implements AsyncIterable { - - _send_seq = 0; - _rec_seq = 0; - _buffer: Buffer | undefined = Buffer.alloc(256); - _reset_deferred: Deferred; - _pending: any; - _sendq: AsyncQueue<{ data: Buffer, seq: number }> - _connected_future: Function | undefined - _readq: AsyncQueue; - // _transport: SerialPort | net.Socket; - _nextIterator: { next: Function }; - _dataFrameReceived: { next: Function }; - logger: any; - - constructor(private writeCb: (data: Buffer) => Promise, private closeCb: () => void, logger: any) { - this.logger = logger; - this._pending = [(- 1), null]; - this._sendq = new AsyncQueue<{ data: Buffer, seq: number }>((iter: { next: Function }) => { - this._nextIterator = iter; - }); - this._readq = new AsyncQueue((iter: { next: Function }) => { - this._dataFrameReceived = iter; - }); - this._send_task(); - } - - data_received(data: Buffer) { - this.logger(`<===== data : ${data.toString('hex')}`); - /* Callback when there is data received from the uart */ - var frame; - if (data.indexOf(CANCEL) >= 0) { - this._buffer = Buffer.alloc(0); - data = data.slice((data.lastIndexOf(CANCEL) + 1)); - } - if (data.indexOf(SUBSTITUTE) >= 0) { - this._buffer = Buffer.alloc(0); - data = data.slice((data.indexOf(FLAG) + 1)); - } - if (this._buffer) { - this._buffer = Buffer.concat([this._buffer, data]); - } else { - this._buffer = data; - } - while (this._buffer) { - let retBuffer; - [frame, retBuffer] = this._extract_frame(this._buffer); - this._buffer = retBuffer as any; - if ((frame === null)) { - break; - } - this.frame_received(frame); - } - } - - _extract_frame(data: Buffer) { - /* Extract a frame from the data buffer */ - const place = data.indexOf(FLAG); - if (place >= 0) { - // todo: check crc data - return [this._unstuff(data.slice(0, (place + 1))), data.slice((place + 1))]; - } - return [null, data]; - } - - frame_received(data: Buffer) { - this.logger(`<===== frame : ${data.toString('hex')}`); - /* Frame receive handler */ - if (((data[0] & 128) === 0)) { - this.data_frame_received(data); - } else { - if (((data[0] & 224) === 128)) { - this.ack_frame_received(data); - } else { - if (((data[0] & 224) === 160)) { - this.nak_frame_received(data); - } else { - if ((data[0] === 192)) { - this.rst_frame_received(data); - } else { - if ((data[0] === 193)) { - this.rstack_frame_received(data); - } else { - if ((data[0] === 194)) { - this.error_frame_received(data); - } else { - this.logger("UNKNOWN FRAME RECEIVED: %r", data); - } - } - } - } - } - } - } - - data_frame_received(data: Buffer) { - /* Data frame receive handler */ - var seq; - this.logger("<===== Received:", data.toString('hex')); - seq = ((data[0] & 112) >> 4); - this._rec_seq = ((seq + 1) % 8); - this.write(this._ack_frame()); - this._handle_ack(data[0]); - if (this._dataFrameReceived) { - this._dataFrameReceived.next(this._randomize(data.slice(1, (- 3)))) - } - } - - [Symbol.asyncIterator]() { - return this._readq; - } - - ack_frame_received(data: Buffer) { - /* Acknowledgement frame receive handler */ - this.logger("ACK frame: %s", data.toString('hex')); - this._handle_ack(data[0]); - } - - nak_frame_received(data: Buffer) { - /* Negative acknowledgement frame receive handler */ - this.logger("NAK frame: %s", data.toString('hex')); - this._handle_nak(data[0]); - } - - rst_frame_received(data: Buffer) { - /* Reset frame handler */ - this.logger("RST frame: %s", data.toString('hex')); - } - - rstack_frame_received(data: Buffer) { - /* Reset acknowledgement frame receive handler */ - var code; - this._send_seq = 0; - this._rec_seq = 0; - try { - code = NcpResetCode[data[2]]; - } catch (e) { - code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; - } - this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); - if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { - return; - } - if ((!this._reset_deferred)) { - this.logger("Reset future is None"); - return; - } - this._reset_deferred.resolve(true); - } - - error_frame_received(data: Buffer) { - /* Error frame receive handler */ - this.logger("Error frame:", data.toString('hex')); - } - - write(data: Buffer) { - /* Send data to the uart */ - this.logger("-----> Sending :", data.toString('hex')); - return this.writeCb(data); - } - - reset() { - /* Sends a reset frame */ - this.logger('uart reseting'); - if ((this._reset_deferred)) { - throw new TypeError("reset can only be called on a new connection"); - } - this.write(this._rst_frame()); - this._reset_deferred = new Deferred(); - return this._reset_deferred.promise; - } - - close() { - this.logger('uart closing'); - return this.closeCb(); - } - - private sleep(ms: number) { - return new Promise(resolve => { - setTimeout(resolve, ms) - }) - } - - async _send_task() { - for await (const item of this._sendq) { - let data, rxmit, seq, success; - //console.log('IteratorResult', item); - if ((item instanceof Terminator)) { - return; - } - if (!item) { - await this.sleep(5000); - continue; - } - data = (item as any).data; - seq = (item as any).seq; - success = false; - rxmit = 0; - while ((!success)) { - this._pending = { seq }; - try { - await this.write(this._data_frame(data, seq, rxmit)); - success = true; - } catch (e) { - rxmit = 1; - success = false; - } - } - } - } - - _handle_ack(control: number) { - /* Handle an acknowledgement frame */ - var ack, pending; - ack = (((control & 7) - 1) % 8); - if ((ack === this._pending[0])) { - [pending, this._pending] = [this._pending, [(- 1), null]]; - pending[1].set_result(true); - } - } - - _handle_nak(control: number) { - /* Handle negative acknowledgment frame */ - let nak = (control & 7); - if ((nak === this._pending[0])) { - this._pending[1].set_result(false); - } - } - - data(data: Buffer) { - /* Send a data frame */ - let seq = this._send_seq; - this._send_seq = ((seq + 1) % 8); - this._nextIterator.next({ data, seq }); - } - - _data_frame(data: Buffer, seq: number, rxmit: number) { - /* Construct a data frame */ - let control; - console.assert(((0 <= seq) && (seq <= 7))); - console.assert(((0 <= rxmit) && (rxmit <= 1))); - control = (((seq << 4) | (rxmit << 3)) | this._rec_seq); - return this._frame([control], this._randomize(data)); - } - - _ack_frame() { - /* Construct a acknowledgement frame */ - let control; - console.assert(((0 <= this._rec_seq) && (this._rec_seq < 8))); - - control = [(0b10000000 | (this._rec_seq & 0b00000111))]; - return this._frame(control); - } - - _rst_frame() { - /* Construct a reset frame */ - return Buffer.concat([Buffer.from([CANCEL]), this._frame([0xC0])]); - } - - _frame(control: ArrayLike, data?: ArrayLike) { - /* Construct a frame */ - const ctrlArr: Array = Array.from(control); - const dataArr: Array = (data && Array.from(data)) || []; - - const sum = ctrlArr.concat(dataArr); - - let crc = crc16ccitt(Buffer.from(sum), 65535); - let crcArr = [(crc >> 8), (crc % 256)]; - return Buffer.concat([this._stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); - } - - _randomize(s: Buffer) { - /*XOR s with a pseudo-random sequence for transmission - Used only in data frames - */ - let rand = RANDOMIZE_START; - let out = Buffer.alloc(s.length); - let outIdx = 0; - for (let c of s){ - out.writeUInt8(c ^ rand, outIdx++); - if ((rand % 2)) { - rand = ((rand >> 1) ^ RANDOMIZE_SEQ); - } else { - rand = (rand >> 1); - } - } - return out; - } - - _stuff(s: Iterable): Buffer { - /* Byte stuff (escape) a string for transmission */ - let out = Buffer.alloc(256); - let outIdx = 0; - for (const c of s) { - if (RESERVED.includes(c)) { - out.writeUInt8(ESCAPE, outIdx++); - out.writeUInt8(c ^ STUFF, outIdx++); - } else { - out.writeUInt8(c, outIdx++); - } - } - return out.slice(0, outIdx); - } - - _unstuff(s: Buffer) { - /* Unstuff (unescape) a string after receipt */ - let escaped = false; - let out = Buffer.alloc(s.length); - let outIdx = 0; - for (let idx = 0; idx < s.length; idx += 1) { - const c = s[idx]; - if (escaped) { - out.writeUInt8(c ^ STUFF, outIdx++); - escaped = false; - } else { - if ((c === ESCAPE)) { - escaped = true; - } else { - out.writeUInt8(c, outIdx++); - } - } - } - return out; - } - - static connect(portAddress: string, connectionOptions: {}, logger: any): Promise { - const portType = SocketPortUtils.isTcpPath(portAddress) ? 'socket' : 'serial'; - let protocol: UartProtocol; - if (portType == 'serial') { - const port = new SerialPort(portAddress, connectionOptions); - protocol = new UartProtocol((data: Buffer) => { - return new Promise((resolve, reject) => { - port.write(data, (err: Error) => { - if (!err) { - resolve(); - } else { - reject(err) - } - }); - }) - }, - () => { - return port.close(); - }, - logger - ); - - port.on('data', (data: any) => protocol.data_received(data)); - - return new Promise((resolve, reject) => { - port.on('open', () => { - logger('port open. resetting'); - protocol.reset().then( - () => { - logger('successfully reset'); - resolve(protocol); - }, (err) => { - logger(err); - reject() - } - ); - }); - port.on('error', (err: any) => { - logger(err); - reject(); - }); - }); - } else { // socket - const info = SocketPortUtils.parseTcpPath(portAddress); - logger(`Opening TCP socket with ${info.host}:${info.port}`); - const socketPort = new net.Socket(); - socketPort.setNoDelay(true); - socketPort.setKeepAlive(true, 15000); - protocol = new UartProtocol((data: Buffer) => { - return new Promise((resolve, reject) => { - socketPort.write(data, (err: Error) => { - if (!err) { - resolve(); - } else { - reject(err) - } - }); - }) - }, - () => { - return socketPort.emit('close'); - }, - logger - ); - // const parser = socketPort.pipe( - // new SerialPort.parsers.ByteLength({length: 1}), - // ); - socketPort.on('data', (data: any) => protocol.data_received(data)); - return new Promise((resolve, reject) => { - socketPort.on('connect', function () { - logger('Socket connected'); - }); - socketPort.on('ready', () => { - logger('socket open'); - protocol.reset().then( - () => { - logger('successfully reset'); - resolve(protocol); - }, (err) => { - logger(err); - reject() - } - ); - }); - socketPort.on('error', (err: any) => { - logger(err); - reject(); - }); - socketPort.connect(info.port, info.host); - }); - } - } -} +import * as stream from 'stream'; +import Debug from "debug"; + +const debug = Debug('zigbee-herdsman:adapter:ezsp:uart'); + +import SerialPort from 'serialport'; +import net from "net"; +import { Deferred, AsyncQueue, crc16ccitt } from './utils'; +import SerialPortUtils from "../../serialPortUtils"; +import SocketPortUtils from "../../socketPortUtils"; + +const FLAG = 0x7E // Marks end of frame +const ESCAPE = 0x7D +const XON = 0x11 // Resume transmission +const XOFF = 0x13 // Stop transmission +const SUBSTITUTE = 0x18 +const CANCEL = 0x1A // Terminates a frame in progress +const STUFF = 0x20 +const RANDOMIZE_START = 0x42 +const RANDOMIZE_SEQ = 0xB8 +const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] + +class Terminator { +} + +enum NcpResetCode { + RESET_UNKNOWN_REASON = 0x00, + RESET_EXTERNAL = 0x01, + RESET_POWER_ON = 0x02, + RESET_WATCHDOG = 0x03, + RESET_ASSERT = 0x06, + RESET_BOOTLOADER = 0x09, + RESET_SOFTWARE = 0x0B, + ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, + ERROR_UNKNOWN_EM3XX_ERROR = 0x80, +} + +export class UartProtocol implements AsyncIterable { + + _send_seq = 0; + _rec_seq = 0; + _buffer: Buffer | undefined = Buffer.alloc(256); + _reset_deferred: Deferred; + _pending: any; + _sendq: AsyncQueue<{ data: Buffer, seq: number }> + _connected_future: Function | undefined + _readq: AsyncQueue; + // _transport: SerialPort | net.Socket; + _nextIterator: { next: Function }; + _dataFrameReceived: { next: Function }; + logger: any; + + constructor(private writeCb: (data: Buffer) => Promise, private closeCb: () => void, logger: any) { + this.logger = logger; + this._pending = [(- 1), null]; + this._sendq = new AsyncQueue<{ data: Buffer, seq: number }>((iter: { next: Function }) => { + this._nextIterator = iter; + }); + this._readq = new AsyncQueue((iter: { next: Function }) => { + this._dataFrameReceived = iter; + }); + this._send_task(); + } + + data_received(data: Buffer) { + this.logger(`<===== data : ${data.toString('hex')}`); + /* Callback when there is data received from the uart */ + var frame; + if (data.indexOf(CANCEL) >= 0) { + this._buffer = Buffer.alloc(0); + data = data.slice((data.lastIndexOf(CANCEL) + 1)); + } + if (data.indexOf(SUBSTITUTE) >= 0) { + this._buffer = Buffer.alloc(0); + data = data.slice((data.indexOf(FLAG) + 1)); + } + if (this._buffer) { + this._buffer = Buffer.concat([this._buffer, data]); + } else { + this._buffer = data; + } + while (this._buffer) { + let retBuffer; + [frame, retBuffer] = this._extract_frame(this._buffer); + this._buffer = retBuffer as any; + if ((frame === null)) { + break; + } + this.frame_received(frame); + } + } + + _extract_frame(data: Buffer) { + /* Extract a frame from the data buffer */ + const place = data.indexOf(FLAG); + if (place >= 0) { + // todo: check crc data + return [this._unstuff(data.slice(0, (place + 1))), data.slice((place + 1))]; + } + return [null, data]; + } + + frame_received(data: Buffer) { + this.logger(`<===== frame : ${data.toString('hex')}`); + /* Frame receive handler */ + if (((data[0] & 128) === 0)) { + this.data_frame_received(data); + } else { + if (((data[0] & 224) === 128)) { + this.ack_frame_received(data); + } else { + if (((data[0] & 224) === 160)) { + this.nak_frame_received(data); + } else { + if ((data[0] === 192)) { + this.rst_frame_received(data); + } else { + if ((data[0] === 193)) { + this.rstack_frame_received(data); + } else { + if ((data[0] === 194)) { + this.error_frame_received(data); + } else { + this.logger("UNKNOWN FRAME RECEIVED: %r", data); + } + } + } + } + } + } + } + + data_frame_received(data: Buffer) { + /* Data frame receive handler */ + var seq; + this.logger("<===== Received:", data.toString('hex')); + seq = ((data[0] & 112) >> 4); + this._rec_seq = ((seq + 1) % 8); + this.write(this._ack_frame()); + this._handle_ack(data[0]); + if (this._dataFrameReceived) { + this._dataFrameReceived.next(this._randomize(data.slice(1, (- 3)))) + } + } + + [Symbol.asyncIterator]() { + return this._readq; + } + + ack_frame_received(data: Buffer) { + /* Acknowledgement frame receive handler */ + this.logger("ACK frame: %s", data.toString('hex')); + this._handle_ack(data[0]); + } + + nak_frame_received(data: Buffer) { + /* Negative acknowledgement frame receive handler */ + this.logger("NAK frame: %s", data.toString('hex')); + this._handle_nak(data[0]); + } + + rst_frame_received(data: Buffer) { + /* Reset frame handler */ + this.logger("RST frame: %s", data.toString('hex')); + } + + rstack_frame_received(data: Buffer) { + /* Reset acknowledgement frame receive handler */ + var code; + this._send_seq = 0; + this._rec_seq = 0; + try { + code = NcpResetCode[data[2]]; + } catch (e) { + code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; + } + this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { + return; + } + if ((!this._reset_deferred)) { + this.logger("Reset future is None"); + return; + } + this._reset_deferred.resolve(true); + } + + error_frame_received(data: Buffer) { + /* Error frame receive handler */ + this.logger("Error frame:", data.toString('hex')); + } + + write(data: Buffer) { + /* Send data to the uart */ + this.logger("-----> Sending :", data.toString('hex')); + return this.writeCb(data); + } + + reset() { + /* Sends a reset frame */ + this.logger('uart reseting'); + if ((this._reset_deferred)) { + throw new TypeError("reset can only be called on a new connection"); + } + this.write(this._rst_frame()); + this._reset_deferred = new Deferred(); + return this._reset_deferred.promise; + } + + close() { + this.logger('uart closing'); + return this.closeCb(); + } + + private sleep(ms: number) { + return new Promise(resolve => { + setTimeout(resolve, ms) + }) + } + + async _send_task() { + for await (const item of this._sendq) { + let data, rxmit, seq, success; + //console.log('IteratorResult', item); + if ((item instanceof Terminator)) { + return; + } + if (!item) { + await this.sleep(5000); + continue; + } + data = (item as any).data; + seq = (item as any).seq; + success = false; + rxmit = 0; + while ((!success)) { + this._pending = { seq }; + try { + await this.write(this._data_frame(data, seq, rxmit)); + success = true; + } catch (e) { + rxmit = 1; + success = false; + } + } + } + } + + _handle_ack(control: number) { + /* Handle an acknowledgement frame */ + var ack, pending; + ack = (((control & 7) - 1) % 8); + if ((ack === this._pending[0])) { + [pending, this._pending] = [this._pending, [(- 1), null]]; + pending[1].set_result(true); + } + } + + _handle_nak(control: number) { + /* Handle negative acknowledgment frame */ + let nak = (control & 7); + if ((nak === this._pending[0])) { + this._pending[1].set_result(false); + } + } + + data(data: Buffer) { + /* Send a data frame */ + let seq = this._send_seq; + this._send_seq = ((seq + 1) % 8); + this._nextIterator.next({ data, seq }); + } + + _data_frame(data: Buffer, seq: number, rxmit: number) { + /* Construct a data frame */ + let control; + console.assert(((0 <= seq) && (seq <= 7))); + console.assert(((0 <= rxmit) && (rxmit <= 1))); + control = (((seq << 4) | (rxmit << 3)) | this._rec_seq); + return this._frame([control], this._randomize(data)); + } + + _ack_frame() { + /* Construct a acknowledgement frame */ + let control; + console.assert(((0 <= this._rec_seq) && (this._rec_seq < 8))); + + control = [(0b10000000 | (this._rec_seq & 0b00000111))]; + return this._frame(control); + } + + _rst_frame() { + /* Construct a reset frame */ + return Buffer.concat([Buffer.from([CANCEL]), this._frame([0xC0])]); + } + + _frame(control: ArrayLike, data?: ArrayLike) { + /* Construct a frame */ + const ctrlArr: Array = Array.from(control); + const dataArr: Array = (data && Array.from(data)) || []; + + const sum = ctrlArr.concat(dataArr); + + let crc = crc16ccitt(Buffer.from(sum), 65535); + let crcArr = [(crc >> 8), (crc % 256)]; + return Buffer.concat([this._stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); + } + + _randomize(s: Buffer) { + /*XOR s with a pseudo-random sequence for transmission + Used only in data frames + */ + let rand = RANDOMIZE_START; + let out = Buffer.alloc(s.length); + let outIdx = 0; + for (let c of s){ + out.writeUInt8(c ^ rand, outIdx++); + if ((rand % 2)) { + rand = ((rand >> 1) ^ RANDOMIZE_SEQ); + } else { + rand = (rand >> 1); + } + } + return out; + } + + _stuff(s: Iterable): Buffer { + /* Byte stuff (escape) a string for transmission */ + let out = Buffer.alloc(256); + let outIdx = 0; + for (const c of s) { + if (RESERVED.includes(c)) { + out.writeUInt8(ESCAPE, outIdx++); + out.writeUInt8(c ^ STUFF, outIdx++); + } else { + out.writeUInt8(c, outIdx++); + } + } + return out.slice(0, outIdx); + } + + _unstuff(s: Buffer) { + /* Unstuff (unescape) a string after receipt */ + let escaped = false; + let out = Buffer.alloc(s.length); + let outIdx = 0; + for (let idx = 0; idx < s.length; idx += 1) { + const c = s[idx]; + if (escaped) { + out.writeUInt8(c ^ STUFF, outIdx++); + escaped = false; + } else { + if ((c === ESCAPE)) { + escaped = true; + } else { + out.writeUInt8(c, outIdx++); + } + } + } + return out; + } + + static connect(portAddress: string, connectionOptions: {}, logger: any): Promise { + const portType = SocketPortUtils.isTcpPath(portAddress) ? 'socket' : 'serial'; + let protocol: UartProtocol; + const writer = new Writer(); + const parser = new Parser(); + if (portType == 'serial') { + const port = new SerialPort(portAddress, connectionOptions); + // @ts-ignore + writer.pipe(port); + port.pipe(parser); + + + protocol = new UartProtocol((data: Buffer) => { + return new Promise((resolve, reject) => { + port.write(data, (err: Error) => { + if (!err) { + resolve(); + } else { + reject(err) + } + }); + }) + }, + () => { + return port.close(); + }, + logger + ); + + //port.on('data', (data: any) => protocol.data_received(data)); + parser.on('parsed', protocol.frame_received); + + return new Promise((resolve, reject) => { + port.on('open', () => { + logger('port open. resetting'); + protocol.reset().then( + () => { + logger('successfully reset'); + resolve(protocol); + }, (err) => { + logger(err); + reject() + } + ); + }); + port.on('error', (err: any) => { + logger(err); + reject(); + }); + }); + } else { // socket + const info = SocketPortUtils.parseTcpPath(portAddress); + logger(`Opening TCP socket with ${info.host}:${info.port}`); + const socketPort = new net.Socket(); + socketPort.setNoDelay(true); + socketPort.setKeepAlive(true, 15000); + writer.pipe(socketPort); + socketPort.pipe(parser); + protocol = new UartProtocol((data: Buffer) => { + return new Promise((resolve, reject) => { + socketPort.write(data, (err: Error) => { + if (!err) { + resolve(); + } else { + reject(err) + } + }); + }) + }, + () => { + return socketPort.emit('close'); + }, + logger + ); + // const parser = socketPort.pipe( + // new SerialPort.parsers.ByteLength({length: 1}), + // ); + //socketPort.on('data', (data: any) => protocol.data_received(data)); + parser.on('parsed', protocol.frame_received); + + return new Promise((resolve, reject) => { + socketPort.on('connect', function () { + logger('Socket connected'); + }); + socketPort.on('ready', () => { + logger('socket open'); + protocol.reset().then( + () => { + logger('successfully reset'); + resolve(protocol); + }, (err) => { + logger(err); + reject() + } + ); + }); + socketPort.on('error', (err: any) => { + logger(err); + reject(); + }); + socketPort.connect(info.port, info.host); + }); + } + } +} + +export class Parser extends stream.Transform { + private buffer: Buffer; + + public constructor() { + super(); + this.buffer = Buffer.from([]); + } + + public _transform(chunk: Buffer, _: string, cb: () => void): void { + debug(`<-- [${[...chunk]}]`); + if (chunk.indexOf(CANCEL) >= 0) { + this.buffer = Buffer.from([]); + chunk = chunk.slice((chunk.lastIndexOf(CANCEL) + 1)); + } + if (chunk.indexOf(SUBSTITUTE) >= 0) { + this.buffer = Buffer.from([]); + chunk = chunk.slice((chunk.indexOf(FLAG) + 1)); + } + this.buffer = Buffer.concat([this.buffer, chunk]); + this.parseNext(); + cb(); + } + + private parseNext(): void { + debug(`--- parseNext [${[...this.buffer]}]`); + if (this.buffer.length && this.buffer.indexOf(FLAG) >= 0) { + debug(`<-- [${this.buffer.toString('hex')}]`); + debug(` [${[...this.buffer]}]`); + try { + const frame = this.extract_frame(); + debug(`<-- parsed ${frame}`); + if (frame) { + this.emit('parsed', frame); + } + } catch (error) { + debug(`<-- error ${error.stack}`); + } + this.parseNext(); + } + } + + private extract_frame(): Buffer { + /* Extract a frame from the data buffer */ + const place = this.buffer.indexOf(FLAG); + if (place >= 0) { + // todo: check crc data + const result = this.unstuff(this.buffer.slice(0, (place + 1))); + this.buffer = this.buffer.slice((place + 1)); + return result; + } else { + return null; + } + } + + private unstuff(s: Buffer): Buffer { + /* Unstuff (unescape) a string after receipt */ + let escaped = false; + let out = Buffer.alloc(s.length); + let outIdx = 0; + for (let idx = 0; idx < s.length; idx += 1) { + const c = s[idx]; + if (escaped) { + out.writeUInt8(c ^ STUFF, outIdx++); + escaped = false; + } else { + if ((c === ESCAPE)) { + escaped = true; + } else { + out.writeUInt8(c, outIdx++); + } + } + } + return out; + } +} + + +export class Writer extends stream.Readable { + public writeBuffer(buffer: Buffer): void { + debug(`--> [${buffer.toString('hex')}]`); + debug(` [${[...buffer]}]`); + this.push(buffer); + } + + public _read(): void {} +} \ No newline at end of file From 01f75bc73ad3f412df971b3493371fac3d3f2fe8 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 29 Jan 2021 20:43:25 +0300 Subject: [PATCH 20/63] rewrited uart --- src/adapter/ezsp/driver/ezsp.ts | 50 +--- src/adapter/ezsp/driver/uart.ts | 489 ++------------------------------ 2 files changed, 37 insertions(+), 502 deletions(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 85287237e0..392d5b122b 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,17 +1,13 @@ import * as t from './types'; -//import { UartProtocol } from './uart'; -import { Writer, Parser } from './uart'; +import { Writer, Parser, FLAG, CANCEL } from './uart'; import { COMMANDS, ZDO_COMMANDS } from './commands'; import { Deferred, crc16ccitt } from './utils'; import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; -import { int_t } from 'zigbee-herdsman/src/adapter/ezsp/driver/types/basic'; import SerialPort from 'serialport'; import net from 'net'; - -import SerialPortUtils from '../../serialPortUtils'; import SocketPortUtils from '../../socketPortUtils'; import Debug from "debug"; @@ -20,17 +16,6 @@ const debug = { log: Debug('zigbee-herdsman:adapter:ezsp:log'), }; -const FLAG = 0x7E // Marks end of frame -const ESCAPE = 0x7D -const XON = 0x11 // Resume transmission -const XOFF = 0x13 // Stop transmission -const SUBSTITUTE = 0x18 -const CANCEL = 0x1A // Terminates a frame in progress -const STUFF = 0x20 -const RANDOMIZE_START = 0x42 -const RANDOMIZE_SEQ = 0xB8 -const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] - enum NcpResetCode { RESET_UNKNOWN_REASON = 0x00, RESET_EXTERNAL = 0x01, @@ -43,6 +28,9 @@ enum NcpResetCode { ERROR_UNKNOWN_EM3XX_ERROR = 0x80, } +const RANDOMIZE_START = 0x42; +const RANDOMIZE_SEQ = 0xB8; + export class Ezsp extends EventEmitter { ezsp_version = 4; @@ -83,7 +71,7 @@ export class Ezsp extends EventEmitter { // this.log(object.type, message); // this.waitress.resolve(object); // this.emit('received', object); - debug.log(`<===== frame : ${data.toString('hex')}`); + //debug.log(`<===== frame : ${data.toString('hex')}`); /* Frame receive handler */ switch (true) { case ((data[0] & 128) === 0): @@ -127,12 +115,14 @@ export class Ezsp extends EventEmitter { var seq; seq = ((data[0] & 112) >> 4); this.recv_seq = ((seq + 1) % 8); + debug.log('send ACK'); this.writer.writeBuffer(this.make_ack_frame()); this.handle_ack(data[0]); data = data.slice(1, (- 3)); - debug.log(data); //this.waitress.resolve(data); - this.emit('received', this.randomize(data)); + //this.emit('received', this.randomize(data)); + const frame = this.randomize(data); + this.frame_received(frame); } private handle_ack(control: number) { @@ -189,7 +179,7 @@ export class Ezsp extends EventEmitter { let crc = crc16ccitt(Buffer.from(sum), 65535); let crcArr = [(crc >> 8), (crc % 256)]; - return Buffer.concat([this.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); + return Buffer.concat([this.writer.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); } private randomize(s: Buffer): Buffer { @@ -210,21 +200,6 @@ export class Ezsp extends EventEmitter { return out; } - private stuff(s: Iterable): Buffer { - /* Byte stuff (escape) a string for transmission */ - let out = Buffer.alloc(256); - let outIdx = 0; - for (const c of s) { - if (RESERVED.includes(c)) { - out.writeUInt8(ESCAPE, outIdx++); - out.writeUInt8(c ^ STUFF, outIdx++); - } else { - out.writeUInt8(c, outIdx++); - } - } - return out.slice(0, outIdx); - } - public isInitialized(): boolean { return this.initialized; } @@ -580,6 +555,7 @@ export class Ezsp extends EventEmitter { just have EZSP application stuff here, with all escaping/stuffing and data randomization removed. */ + debug.log(`<=== Frame: ${data.toString('hex')}`); var frame_id: number, result, schema, sequence; if ((this.ezsp_version < 8)) { [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; @@ -597,14 +573,14 @@ export class Ezsp extends EventEmitter { let cmd = this.COMMANDS_BY_ID.get(frame_id); if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); let frameName = cmd.name; - this.logger("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); + debug.log("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); if (this._awaiting.has(sequence)) { let entry = this._awaiting.get(sequence); this._awaiting.delete(sequence); if (entry) { console.assert(entry.expectedId === frame_id); [result, data] = t.deserialize(data, entry.schema); - this.logger(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); + debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); entry.deferred.resolve(result); } } else { diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index eabca5b572..3782056a79 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -3,468 +3,15 @@ import Debug from "debug"; const debug = Debug('zigbee-herdsman:adapter:ezsp:uart'); -import SerialPort from 'serialport'; -import net from "net"; -import { Deferred, AsyncQueue, crc16ccitt } from './utils'; -import SerialPortUtils from "../../serialPortUtils"; -import SocketPortUtils from "../../socketPortUtils"; - -const FLAG = 0x7E // Marks end of frame -const ESCAPE = 0x7D +export const FLAG = 0x7E // Marks end of frame +export const ESCAPE = 0x7D +export const CANCEL = 0x1A // Terminates a frame in progress const XON = 0x11 // Resume transmission const XOFF = 0x13 // Stop transmission const SUBSTITUTE = 0x18 -const CANCEL = 0x1A // Terminates a frame in progress const STUFF = 0x20 -const RANDOMIZE_START = 0x42 -const RANDOMIZE_SEQ = 0xB8 -const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] - -class Terminator { -} - -enum NcpResetCode { - RESET_UNKNOWN_REASON = 0x00, - RESET_EXTERNAL = 0x01, - RESET_POWER_ON = 0x02, - RESET_WATCHDOG = 0x03, - RESET_ASSERT = 0x06, - RESET_BOOTLOADER = 0x09, - RESET_SOFTWARE = 0x0B, - ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, - ERROR_UNKNOWN_EM3XX_ERROR = 0x80, -} - -export class UartProtocol implements AsyncIterable { - - _send_seq = 0; - _rec_seq = 0; - _buffer: Buffer | undefined = Buffer.alloc(256); - _reset_deferred: Deferred; - _pending: any; - _sendq: AsyncQueue<{ data: Buffer, seq: number }> - _connected_future: Function | undefined - _readq: AsyncQueue; - // _transport: SerialPort | net.Socket; - _nextIterator: { next: Function }; - _dataFrameReceived: { next: Function }; - logger: any; - - constructor(private writeCb: (data: Buffer) => Promise, private closeCb: () => void, logger: any) { - this.logger = logger; - this._pending = [(- 1), null]; - this._sendq = new AsyncQueue<{ data: Buffer, seq: number }>((iter: { next: Function }) => { - this._nextIterator = iter; - }); - this._readq = new AsyncQueue((iter: { next: Function }) => { - this._dataFrameReceived = iter; - }); - this._send_task(); - } - - data_received(data: Buffer) { - this.logger(`<===== data : ${data.toString('hex')}`); - /* Callback when there is data received from the uart */ - var frame; - if (data.indexOf(CANCEL) >= 0) { - this._buffer = Buffer.alloc(0); - data = data.slice((data.lastIndexOf(CANCEL) + 1)); - } - if (data.indexOf(SUBSTITUTE) >= 0) { - this._buffer = Buffer.alloc(0); - data = data.slice((data.indexOf(FLAG) + 1)); - } - if (this._buffer) { - this._buffer = Buffer.concat([this._buffer, data]); - } else { - this._buffer = data; - } - while (this._buffer) { - let retBuffer; - [frame, retBuffer] = this._extract_frame(this._buffer); - this._buffer = retBuffer as any; - if ((frame === null)) { - break; - } - this.frame_received(frame); - } - } - - _extract_frame(data: Buffer) { - /* Extract a frame from the data buffer */ - const place = data.indexOf(FLAG); - if (place >= 0) { - // todo: check crc data - return [this._unstuff(data.slice(0, (place + 1))), data.slice((place + 1))]; - } - return [null, data]; - } - - frame_received(data: Buffer) { - this.logger(`<===== frame : ${data.toString('hex')}`); - /* Frame receive handler */ - if (((data[0] & 128) === 0)) { - this.data_frame_received(data); - } else { - if (((data[0] & 224) === 128)) { - this.ack_frame_received(data); - } else { - if (((data[0] & 224) === 160)) { - this.nak_frame_received(data); - } else { - if ((data[0] === 192)) { - this.rst_frame_received(data); - } else { - if ((data[0] === 193)) { - this.rstack_frame_received(data); - } else { - if ((data[0] === 194)) { - this.error_frame_received(data); - } else { - this.logger("UNKNOWN FRAME RECEIVED: %r", data); - } - } - } - } - } - } - } - - data_frame_received(data: Buffer) { - /* Data frame receive handler */ - var seq; - this.logger("<===== Received:", data.toString('hex')); - seq = ((data[0] & 112) >> 4); - this._rec_seq = ((seq + 1) % 8); - this.write(this._ack_frame()); - this._handle_ack(data[0]); - if (this._dataFrameReceived) { - this._dataFrameReceived.next(this._randomize(data.slice(1, (- 3)))) - } - } - - [Symbol.asyncIterator]() { - return this._readq; - } - - ack_frame_received(data: Buffer) { - /* Acknowledgement frame receive handler */ - this.logger("ACK frame: %s", data.toString('hex')); - this._handle_ack(data[0]); - } - - nak_frame_received(data: Buffer) { - /* Negative acknowledgement frame receive handler */ - this.logger("NAK frame: %s", data.toString('hex')); - this._handle_nak(data[0]); - } - - rst_frame_received(data: Buffer) { - /* Reset frame handler */ - this.logger("RST frame: %s", data.toString('hex')); - } - - rstack_frame_received(data: Buffer) { - /* Reset acknowledgement frame receive handler */ - var code; - this._send_seq = 0; - this._rec_seq = 0; - try { - code = NcpResetCode[data[2]]; - } catch (e) { - code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; - } - this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); - if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { - return; - } - if ((!this._reset_deferred)) { - this.logger("Reset future is None"); - return; - } - this._reset_deferred.resolve(true); - } - - error_frame_received(data: Buffer) { - /* Error frame receive handler */ - this.logger("Error frame:", data.toString('hex')); - } - - write(data: Buffer) { - /* Send data to the uart */ - this.logger("-----> Sending :", data.toString('hex')); - return this.writeCb(data); - } - - reset() { - /* Sends a reset frame */ - this.logger('uart reseting'); - if ((this._reset_deferred)) { - throw new TypeError("reset can only be called on a new connection"); - } - this.write(this._rst_frame()); - this._reset_deferred = new Deferred(); - return this._reset_deferred.promise; - } - - close() { - this.logger('uart closing'); - return this.closeCb(); - } - - private sleep(ms: number) { - return new Promise(resolve => { - setTimeout(resolve, ms) - }) - } - - async _send_task() { - for await (const item of this._sendq) { - let data, rxmit, seq, success; - //console.log('IteratorResult', item); - if ((item instanceof Terminator)) { - return; - } - if (!item) { - await this.sleep(5000); - continue; - } - data = (item as any).data; - seq = (item as any).seq; - success = false; - rxmit = 0; - while ((!success)) { - this._pending = { seq }; - try { - await this.write(this._data_frame(data, seq, rxmit)); - success = true; - } catch (e) { - rxmit = 1; - success = false; - } - } - } - } - - _handle_ack(control: number) { - /* Handle an acknowledgement frame */ - var ack, pending; - ack = (((control & 7) - 1) % 8); - if ((ack === this._pending[0])) { - [pending, this._pending] = [this._pending, [(- 1), null]]; - pending[1].set_result(true); - } - } - - _handle_nak(control: number) { - /* Handle negative acknowledgment frame */ - let nak = (control & 7); - if ((nak === this._pending[0])) { - this._pending[1].set_result(false); - } - } - - data(data: Buffer) { - /* Send a data frame */ - let seq = this._send_seq; - this._send_seq = ((seq + 1) % 8); - this._nextIterator.next({ data, seq }); - } - - _data_frame(data: Buffer, seq: number, rxmit: number) { - /* Construct a data frame */ - let control; - console.assert(((0 <= seq) && (seq <= 7))); - console.assert(((0 <= rxmit) && (rxmit <= 1))); - control = (((seq << 4) | (rxmit << 3)) | this._rec_seq); - return this._frame([control], this._randomize(data)); - } +export const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] - _ack_frame() { - /* Construct a acknowledgement frame */ - let control; - console.assert(((0 <= this._rec_seq) && (this._rec_seq < 8))); - - control = [(0b10000000 | (this._rec_seq & 0b00000111))]; - return this._frame(control); - } - - _rst_frame() { - /* Construct a reset frame */ - return Buffer.concat([Buffer.from([CANCEL]), this._frame([0xC0])]); - } - - _frame(control: ArrayLike, data?: ArrayLike) { - /* Construct a frame */ - const ctrlArr: Array = Array.from(control); - const dataArr: Array = (data && Array.from(data)) || []; - - const sum = ctrlArr.concat(dataArr); - - let crc = crc16ccitt(Buffer.from(sum), 65535); - let crcArr = [(crc >> 8), (crc % 256)]; - return Buffer.concat([this._stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); - } - - _randomize(s: Buffer) { - /*XOR s with a pseudo-random sequence for transmission - Used only in data frames - */ - let rand = RANDOMIZE_START; - let out = Buffer.alloc(s.length); - let outIdx = 0; - for (let c of s){ - out.writeUInt8(c ^ rand, outIdx++); - if ((rand % 2)) { - rand = ((rand >> 1) ^ RANDOMIZE_SEQ); - } else { - rand = (rand >> 1); - } - } - return out; - } - - _stuff(s: Iterable): Buffer { - /* Byte stuff (escape) a string for transmission */ - let out = Buffer.alloc(256); - let outIdx = 0; - for (const c of s) { - if (RESERVED.includes(c)) { - out.writeUInt8(ESCAPE, outIdx++); - out.writeUInt8(c ^ STUFF, outIdx++); - } else { - out.writeUInt8(c, outIdx++); - } - } - return out.slice(0, outIdx); - } - - _unstuff(s: Buffer) { - /* Unstuff (unescape) a string after receipt */ - let escaped = false; - let out = Buffer.alloc(s.length); - let outIdx = 0; - for (let idx = 0; idx < s.length; idx += 1) { - const c = s[idx]; - if (escaped) { - out.writeUInt8(c ^ STUFF, outIdx++); - escaped = false; - } else { - if ((c === ESCAPE)) { - escaped = true; - } else { - out.writeUInt8(c, outIdx++); - } - } - } - return out; - } - - static connect(portAddress: string, connectionOptions: {}, logger: any): Promise { - const portType = SocketPortUtils.isTcpPath(portAddress) ? 'socket' : 'serial'; - let protocol: UartProtocol; - const writer = new Writer(); - const parser = new Parser(); - if (portType == 'serial') { - const port = new SerialPort(portAddress, connectionOptions); - // @ts-ignore - writer.pipe(port); - port.pipe(parser); - - - protocol = new UartProtocol((data: Buffer) => { - return new Promise((resolve, reject) => { - port.write(data, (err: Error) => { - if (!err) { - resolve(); - } else { - reject(err) - } - }); - }) - }, - () => { - return port.close(); - }, - logger - ); - - //port.on('data', (data: any) => protocol.data_received(data)); - parser.on('parsed', protocol.frame_received); - - return new Promise((resolve, reject) => { - port.on('open', () => { - logger('port open. resetting'); - protocol.reset().then( - () => { - logger('successfully reset'); - resolve(protocol); - }, (err) => { - logger(err); - reject() - } - ); - }); - port.on('error', (err: any) => { - logger(err); - reject(); - }); - }); - } else { // socket - const info = SocketPortUtils.parseTcpPath(portAddress); - logger(`Opening TCP socket with ${info.host}:${info.port}`); - const socketPort = new net.Socket(); - socketPort.setNoDelay(true); - socketPort.setKeepAlive(true, 15000); - writer.pipe(socketPort); - socketPort.pipe(parser); - protocol = new UartProtocol((data: Buffer) => { - return new Promise((resolve, reject) => { - socketPort.write(data, (err: Error) => { - if (!err) { - resolve(); - } else { - reject(err) - } - }); - }) - }, - () => { - return socketPort.emit('close'); - }, - logger - ); - // const parser = socketPort.pipe( - // new SerialPort.parsers.ByteLength({length: 1}), - // ); - //socketPort.on('data', (data: any) => protocol.data_received(data)); - parser.on('parsed', protocol.frame_received); - - return new Promise((resolve, reject) => { - socketPort.on('connect', function () { - logger('Socket connected'); - }); - socketPort.on('ready', () => { - logger('socket open'); - protocol.reset().then( - () => { - logger('successfully reset'); - resolve(protocol); - }, (err) => { - logger(err); - reject() - } - ); - }); - socketPort.on('error', (err: any) => { - logger(err); - reject(); - }); - socketPort.connect(info.port, info.host); - }); - } - } -} export class Parser extends stream.Transform { private buffer: Buffer; @@ -475,7 +22,7 @@ export class Parser extends stream.Transform { } public _transform(chunk: Buffer, _: string, cb: () => void): void { - debug(`<-- [${[...chunk]}]`); + //debug(`<-- [${[...chunk]}]`); if (chunk.indexOf(CANCEL) >= 0) { this.buffer = Buffer.from([]); chunk = chunk.slice((chunk.lastIndexOf(CANCEL) + 1)); @@ -490,13 +37,12 @@ export class Parser extends stream.Transform { } private parseNext(): void { - debug(`--- parseNext [${[...this.buffer]}]`); + //debug(`--- parseNext [${[...this.buffer]}]`); if (this.buffer.length && this.buffer.indexOf(FLAG) >= 0) { - debug(`<-- [${this.buffer.toString('hex')}]`); - debug(` [${[...this.buffer]}]`); + debug(`<-- [${this.buffer.toString('hex')}] [${[...this.buffer]}]`); try { const frame = this.extract_frame(); - debug(`<-- parsed ${frame}`); + debug(`<-- parsed ${frame.toString('hex')}`); if (frame) { this.emit('parsed', frame); } @@ -542,13 +88,26 @@ export class Parser extends stream.Transform { } } - export class Writer extends stream.Readable { public writeBuffer(buffer: Buffer): void { - debug(`--> [${buffer.toString('hex')}]`); - debug(` [${[...buffer]}]`); + debug(`--> [${buffer.toString('hex')}] [${[...buffer]}]`); this.push(buffer); } public _read(): void {} + + public stuff(s: Iterable): Buffer { + /* Byte stuff (escape) a string for transmission */ + let out = Buffer.alloc(256); + let outIdx = 0; + for (const c of s) { + if (RESERVED.includes(c)) { + out.writeUInt8(ESCAPE, outIdx++); + out.writeUInt8(c ^ STUFF, outIdx++); + } else { + out.writeUInt8(c, outIdx++); + } + } + return out.slice(0, outIdx); + } } \ No newline at end of file From 31dcb2aae0a3977fc1200f28f01f0c1faf501b2e Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 29 Jan 2021 21:24:00 +0300 Subject: [PATCH 21/63] some cleanup --- src/adapter/ezsp/adapter/ezspAdapter.ts | 4 +-- src/adapter/ezsp/driver/driver.ts | 47 ++++++++++++------------- src/adapter/ezsp/driver/ezsp.ts | 38 ++++++-------------- 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 890b6ed7dd..25105fce46 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -69,7 +69,7 @@ class EZSPAdapter extends Adapter { this.emit('event', frame); } - private handleDeviceJoin(arr: any[]) { + private async handleDeviceJoin(arr: any[]) { // todo let [nwk, ieee] = arr; debug('Device join request received: %s %s', nwk, ieee); @@ -79,7 +79,7 @@ class EZSPAdapter extends Adapter { }; if (nwk == 0) { - //const nd = this.nodeDescriptor(nwk); + const nd = await this.nodeDescriptor(nwk); } else { this.emit(Events.Events.deviceJoined, payload); } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 936219389d..65083206fe 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -28,7 +28,7 @@ type EmberWaitressMatcher = { export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT - private _ezsp: Ezsp; + private ezsp: Ezsp; private _nwkOpt: TsType.NetworkOptions; public networkParams: EmberNetworkParameters; private eui64ToNodeId = new Map(); @@ -56,7 +56,7 @@ export class Driver extends EventEmitter { public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions, logger: any) { this.logger = logger; this._nwkOpt = nwkOpt; - let ezsp = this._ezsp = new Ezsp(this.logger); + let ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); const version = await ezsp.version(); console.log('Got version', version); @@ -106,7 +106,7 @@ export class Driver extends EventEmitter { console.assert(status == EmberStatus.SUCCESS); if (nodeType != EmberNodeType.COORDINATOR) { this.logger(`Leaving current network as ${nodeType} and forming new network`); - const [st] = await this._ezsp.leaveNetwork(); + const [st] = await this.ezsp.leaveNetwork(); console.assert(st == EmberStatus.NETWORK_DOWN); await this.form_network(); [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); @@ -119,25 +119,24 @@ export class Driver extends EventEmitter { const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; - const [ieee] = await this._ezsp.execCommand('getEui64'); + const [ieee] = await this.ezsp.execCommand('getEui64'); this._ieee = ieee; console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) - this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); - this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); this._multicast = new Multicast(ezsp, logger); await this._multicast.startup([]); + // this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); } private async form_network() { const panID = this._nwkOpt.panID; const extendedPanID = this._nwkOpt.extendedPanID; - const hashed_tclk = this._ezsp.ezsp_version > 4; + const hashed_tclk = this.ezsp.ezsp_version > 4; const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt, true, hashed_tclk); - const [status] = await this._ezsp.setInitialSecurityState(initial_security_state); + const [status] = await this.ezsp.setInitialSecurityState(initial_security_state); const parameters:EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; parameters.extendedPanId = extendedPanID; @@ -148,8 +147,8 @@ export class Driver extends EventEmitter { parameters.nwkUpdateId = 0; parameters.channels = 0x07FFF800; // all channels - await this._ezsp.formNetwork(parameters); - await this._ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); + await this.ezsp.formNetwork(parameters); + await this.ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); } private handleFrame(frameName: string, ...args: any[]) { @@ -237,7 +236,7 @@ export class Driver extends EventEmitter { let strEui64 = eui64.toString(); let nodeId = this.eui64ToNodeId.get(strEui64); if (nodeId === undefined) { - nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64); + nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64); if (nodeId && nodeId !== 0xFFFF) { this.eui64ToNodeId.set(strEui64, nodeId); } else { @@ -248,9 +247,9 @@ export class Driver extends EventEmitter { } else { eui64 = await this.networkIdToEUI64(nwk); } - await this._ezsp.execCommand('setExtendedTimeout', eui64, true); + await this.ezsp.execCommand('setExtendedTimeout', eui64, true); - let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); console.log('unicast message sent, waiting for reply'); return true; } catch (e) { @@ -281,7 +280,7 @@ export class Driver extends EventEmitter { // let strEui64 = eui64.toString(); // let nodeId = this.eui64ToNodeId.get(strEui64); // if (nodeId === undefined) { - // nodeId = await this._ezsp.execCommand('lookupNodeIdByEui64', eui64); + // nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64); // if (nodeId && nodeId !== 0xFFFF) { // this.eui64ToNodeId.set(strEui64, nodeId); // } else { @@ -292,9 +291,9 @@ export class Driver extends EventEmitter { // } else { // eui64 = await this.networkIdToEUI64(nwk); // } - // //await this._ezsp.execCommand('setExtendedTimeout', eui64, true); + // //await this.ezsp.execCommand('setExtendedTimeout', eui64, true); - // let v = await this._ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + // let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); // console.log('unicast message sent, waiting for reply'); // if (v[0] != 0) { // this.pending.delete(seq); @@ -354,18 +353,18 @@ export class Driver extends EventEmitter { } public stop() { - return this._ezsp.close(); + return this.ezsp.close(); } public getLocalEUI64(): Promise { - return this._ezsp.execCommand('getEui64'); + return this.ezsp.execCommand('getEui64'); } public async networkIdToEUI64(nwk: number): Promise { for (let [eUI64, value] of this.eui64ToNodeId) { if (value === nwk) return new EmberEUI64(eUI64); } - let value = await this._ezsp.execCommand('lookupEui64ByNodeId', nwk); + let value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); if (value[0] === EmberStatus.SUCCESS) { let eUI64 = new EmberEUI64(value[1] as any); this.eui64ToNodeId.set(eUI64.toString(), nwk); @@ -376,21 +375,21 @@ export class Driver extends EventEmitter { } public async permitJoining(seconds:number){ - const [status] = await this._ezsp.execCommand('setPolicy', EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); + const [status] = await this.ezsp.execCommand('setPolicy', EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); console.assert(status == EmberStatus.SUCCESS); - return await this._ezsp.execCommand('permitJoining', seconds); + return await this.ezsp.execCommand('permitJoining', seconds); } public make_zdo_frame(name: string, ...args: any[]) { - return this._ezsp.make_zdo_frame(name, ...args); + return this.ezsp.make_zdo_frame(name, ...args); } public parse_frame_payload(name: string, obj: Buffer) { - return this._ezsp.parse_frame_payload(name, obj); + return this.ezsp.parse_frame_payload(name, obj); } public async addEndpoint({endpoint=1, profileId=260, deviceId=0xBEEF, appFlags=0, inputClusters=[], outputClusters=[]}: AddEndpointParameters) { - const res = await this._ezsp.execCommand('addEndpoint', + const res = await this.ezsp.execCommand('addEndpoint', endpoint, profileId, deviceId, diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 392d5b122b..6225b9339f 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -34,7 +34,6 @@ const RANDOMIZE_SEQ = 0xB8; export class Ezsp extends EventEmitter { ezsp_version = 4; - //_gw: UartProtocol; _seq = 0; send_seq = 0; recv_seq = 0; @@ -43,7 +42,6 @@ export class Ezsp extends EventEmitter { COMMANDS_BY_ID = new Map(); _cbCounter = 0; reset_deferred: Deferred; - logger: any; private portType: 'serial' | 'socket'; private serialPort: SerialPort; private socketPort: net.Socket; @@ -51,10 +49,9 @@ export class Ezsp extends EventEmitter { private parser: Parser; private initialized: boolean; - constructor(logger: any) { + constructor() { super(); this.initialized = false; - this.logger = logger; for (let name in COMMANDS) { let details = (COMMANDS)[name]; this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); @@ -153,12 +150,12 @@ export class Ezsp extends EventEmitter { } catch (e) { code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; } - this.logger("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + debug.log("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { return; } if ((!this.reset_deferred)) { - this.logger("Reset future is None"); + debug.log("Reset future is None"); return; } this.reset_deferred.resolve(true); @@ -211,10 +208,7 @@ export class Ezsp extends EventEmitter { } async connect(path: string, options: {}) { - //console.assert(!this._gw); this.portType = SocketPortUtils.isTcpPath(path) ? 'socket' : 'serial'; - //this._gw = await UartProtocol.connect(device, options, this.logger); - //this.startReadQueue(); this.portType === 'serial' ? await this.openSerialPort(path, options) : await this.openSocketPort(path, options); } @@ -322,16 +316,6 @@ export class Ezsp extends EventEmitter { return this.make_frame([control], this.randomize(data)); } - // private async startReadQueue() { - // for await (let frame of this._gw) { - // try { - // this.frame_received(frame); - // } catch (e) { - // this.logger('Error handling frame', e); - // } - // } - // } - async reset() { // return this._gw.reset(); debug.log('uart reseting'); @@ -350,7 +334,7 @@ export class Ezsp extends EventEmitter { let version = this.ezsp_version; let result = await this._command("version", version); if ((result[0] !== version)) { - this.logger("Switching to eszp version %d", result[0]); + debug.log("Switching to eszp version %d", result[0]); await this._command("version", result[0]); } return result[0]; @@ -373,12 +357,12 @@ export class Ezsp extends EventEmitter { }) v = await this._command("leaveNetwork"); if ((v[0] !== EmberStatus.SUCCESS)) { - this.logger("Failure to leave network:" + v); + debug.log("Failure to leave network:" + v); throw new Error(("Failure to leave network:" + v)); } v = await fut.promise; if ((v !== EmberStatus.NETWORK_DOWN)) { - this.logger("Failure to leave network:" + v); + debug.log("Failure to leave network:" + v); throw new Error(("Failure to leave network:" + v)); } return v; @@ -388,14 +372,14 @@ export class Ezsp extends EventEmitter { let ret; [ret] = await this.execCommand('setConfigurationValue', configId, value); console.assert(ret === EmberStatus.SUCCESS); - this.logger('Set %s = %s', configId, value); + debug.log('Set %s = %s', configId, value); } async getConfigurationValue(configId: number) { let ret, value; [ret, value] = await this.execCommand('getConfigurationValue', configId); console.assert(ret === EmberStatus.SUCCESS); - this.logger('Get %s = %s', configId, value); + debug.log('Get %s = %s', configId, value); return value; } @@ -530,12 +514,12 @@ export class Ezsp extends EventEmitter { }) v = await this._command("formNetwork", parameters); if ((v[0] !== EmberStatus.SUCCESS)) { - this.logger("Failure forming network:" + v); + debug.log("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } v = await fut.promise; if ((v !== EmberStatus.NETWORK_UP)) { - this.logger("Failure forming network:" + v); + debug.log("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); } return v; @@ -587,7 +571,7 @@ export class Ezsp extends EventEmitter { schema = cmd.outArgs; frameName = cmd.name; [result, data] = t.deserialize(data, schema); - this.logger(`<=== Application frame ${frame_id} (${frameName}): ${result}`); + debug.log(`<=== Application frame ${frame_id} (${frameName}): ${result}`); super.emit('frame', frameName, ...result); } if ((frame_id === 0)) { From 8587f4113408ee121f24f237f523a7e521dfae0d Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sat, 30 Jan 2021 14:17:02 +0300 Subject: [PATCH 22/63] queue --- src/adapter/ezsp/adapter/ezspAdapter.ts | 34 +++--- src/adapter/ezsp/driver/driver.ts | 13 +- src/adapter/ezsp/driver/ezsp.ts | 153 ++++++++++++------------ src/adapter/ezsp/driver/multicast.ts | 28 +++-- 4 files changed, 118 insertions(+), 110 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 25105fce46..29c4157ac6 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -182,22 +182,24 @@ class EZSPAdapter extends Adapter { } private async nodeDescriptorInternal(networkAddress: number): Promise { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Node_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); - const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - await this.driver.request(networkAddress, frame, payload); - const descriptor = await response.start().promise; - debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); - const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); - debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); - return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Node_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); + const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + await this.driver.request(networkAddress, frame, payload); + const descriptor = await response.start().promise; + debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); + const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); + debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); + return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + }); } public async activeEndpoints(networkAddress: number): Promise { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 65083206fe..ca2a7381c7 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -7,7 +7,7 @@ import { EmberObject } from './types/emberObj'; import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId } from './types/named'; import { Multicast } from './multicast'; -import Waitress from "../../../utils/waitress"; +import {Queue, Waitress, Wait} from '../../../utils'; interface AddEndpointParameters { endpoint?: number, @@ -28,7 +28,7 @@ type EmberWaitressMatcher = { export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT - private ezsp: Ezsp; + public ezsp: Ezsp; private _nwkOpt: TsType.NetworkOptions; public networkParams: EmberNetworkParameters; private eui64ToNodeId = new Map(); @@ -38,8 +38,9 @@ export class Driver extends EventEmitter { private _ieee: EmberEUI64; private _multicast: Multicast; private waitress: Waitress; + public queue: Queue; - constructor(nodeInfo?:Iterable<{nodeId:number, eui64: string | EmberEUI64}>){ + constructor(){ super(); // if (!nodeInfo) return; @@ -48,6 +49,7 @@ export class Driver extends EventEmitter { // let eui64 = node.eui64 instanceof EmberEUI64 ? node.eui64 : new EmberEUI64(node.eui64); // this.eui64ToNodeId.set(eui64.toString(), node.nodeId); // } + this.queue = new Queue(); this.waitress = new Waitress( this.waitressValidator, this.waitressTimeoutFormatter); @@ -123,12 +125,11 @@ export class Driver extends EventEmitter { this._ieee = ieee; console.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) - + this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); - this._multicast = new Multicast(ezsp, logger); + this._multicast = new Multicast(this, logger); await this._multicast.startup([]); - // this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); } private async form_network() { diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 6225b9339f..28dde4b10b 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -56,9 +56,12 @@ export class Ezsp extends EventEmitter { let details = (COMMANDS)[name]; this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); } + this.onParsed = this.onParsed.bind(this); this.onPortClose = this.onPortClose.bind(this); } + + //////////////////////// serial routines //////////////////////// private onParsed(data: Buffer): void { try { @@ -330,6 +333,79 @@ export class Ezsp extends EventEmitter { return this.reset_deferred.promise; } + public close(): Promise { + return new Promise((resolve, reject): void => { + if (this.initialized) { + if (this.portType === 'serial') { + this.serialPort.flush((): void => { + this.serialPort.close((error): void => { + this.initialized = false; + error == null ? + resolve() : + reject(new Error(`Error while closing serialport '${error}'`)); + this.emit('close'); + }); + }); + } else { + this.socketPort.destroy(); + resolve(); + } + } else { + resolve(); + this.emit('close'); + } + }); + } + + //////////////////////// command routines //////////////////////// + + private frame_received(data: Buffer) { + /*Handle a received EZSP frame + + The protocol has taken care of UART specific framing etc, so we should + just have EZSP application stuff here, with all escaping/stuffing and + data randomization removed. + */ + debug.log(`<=== Frame: ${data.toString('hex')}`); + var frame_id: number, result, schema, sequence; + if ((this.ezsp_version < 8)) { + [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; + } else { + sequence = data[0]; + [[frame_id], data] = t.deserialize(data.slice(3), [t.uint16_t]); + } + if ((frame_id === 255)) { + frame_id = 0; + if ((data.length > 1)) { + frame_id = data[1]; + data = data.slice(2); + } + } + let cmd = this.COMMANDS_BY_ID.get(frame_id); + if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); + let frameName = cmd.name; + debug.log("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); + if (this._awaiting.has(sequence)) { + let entry = this._awaiting.get(sequence); + this._awaiting.delete(sequence); + if (entry) { + console.assert(entry.expectedId === frame_id); + [result, data] = t.deserialize(data, entry.schema); + debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); + entry.deferred.resolve(result); + } + } else { + schema = cmd.outArgs; + frameName = cmd.name; + [result, data] = t.deserialize(data, schema); + debug.log(`<=== Application frame ${frame_id} (${frameName}): ${result}`); + super.emit('frame', frameName, ...result); + } + if ((frame_id === 0)) { + this.ezsp_version = result[0]; + } + } + async version() { let version = this.ezsp_version; let result = await this._command("version", version); @@ -438,40 +514,14 @@ export class Ezsp extends EventEmitter { console.assert(status == EmberStatus.SUCCESS); } } - - // close() { - // return this._gw.close(); - // } - public close(): Promise { - return new Promise((resolve, reject): void => { - if (this.initialized) { - if (this.portType === 'serial') { - this.serialPort.flush((): void => { - this.serialPort.close((error): void => { - this.initialized = false; - error == null ? - resolve() : - reject(new Error(`Error while closing serialport '${error}'`)); - this.emit('close'); - }); - }); - } else { - this.socketPort.destroy(); - resolve(); - } - } else { - resolve(); - this.emit('close'); - } - }); - } - + public make_zdo_frame(name: string, ...args: any[]): Buffer { var c, data, frame, cmd_id; c = (ZDO_COMMANDS)[name]; data = t.serialize(args, c[1]); return data; } + private _ezsp_frame(name: string, ...args: any[]) { var c, data, frame, cmd_id; c = (COMMANDS)[name]; @@ -532,53 +582,6 @@ export class Ezsp extends EventEmitter { return this._command(name, ...args); } - frame_received(data: Buffer) { - /*Handle a received EZSP frame - - The protocol has taken care of UART specific framing etc, so we should - just have EZSP application stuff here, with all escaping/stuffing and - data randomization removed. - */ - debug.log(`<=== Frame: ${data.toString('hex')}`); - var frame_id: number, result, schema, sequence; - if ((this.ezsp_version < 8)) { - [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; - } else { - sequence = data[0]; - [[frame_id], data] = t.deserialize(data.slice(3), [t.uint16_t]); - } - if ((frame_id === 255)) { - frame_id = 0; - if ((data.length > 1)) { - frame_id = data[1]; - data = data.slice(2); - } - } - let cmd = this.COMMANDS_BY_ID.get(frame_id); - if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); - let frameName = cmd.name; - debug.log("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); - if (this._awaiting.has(sequence)) { - let entry = this._awaiting.get(sequence); - this._awaiting.delete(sequence); - if (entry) { - console.assert(entry.expectedId === frame_id); - [result, data] = t.deserialize(data, entry.schema); - debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); - entry.deferred.resolve(result); - } - } else { - schema = cmd.outArgs; - frameName = cmd.name; - [result, data] = t.deserialize(data, schema); - debug.log(`<=== Application frame ${frame_id} (${frameName}): ${result}`); - super.emit('frame', frameName, ...result); - } - if ((frame_id === 0)) { - this.ezsp_version = result[0]; - } - } - public parse_frame_payload(name: string, data: Buffer) { if (Object.keys(ZDO_COMMANDS).indexOf(name) < 0) { throw new Error('Unknown ZDO command: ' + name); diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 28005f53b4..38b7bf4dad 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -1,4 +1,4 @@ -import { Ezsp } from './ezsp'; +import { Driver } from './driver'; import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; import * as t from './types/basic'; import { EmberStatus, EmberOutgoingMessageType, EmberMulticastId } from './types/named'; @@ -7,25 +7,25 @@ import { EmberMulticastTableEntry } from './types/struct'; export class Multicast { TABLE_SIZE = 16; - private _ezsp: Ezsp; + private driver: Driver; private logger: any; private _multicast: any; private _available: Array; - constructor(ezsp: Ezsp, logger: any){ - this._ezsp = ezsp; + constructor(driver: Driver, logger: any){ + this.driver = driver; this.logger = logger; this._multicast = {}; this._available = []; } private async _initialize() { - const size = await this._ezsp.getConfigurationValue( + const size = await this.driver.ezsp.getConfigurationValue( EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE ); for (let i = 0; i < size; i++) { let st: any, entry: any; - [st, entry] = await this._ezsp.getMulticastTableEntry(i); + [st, entry] = await this.driver.ezsp.getMulticastTableEntry(i); if (st !== EmberStatus.SUCCESS) { this.logger("Couldn't get MulticastTableEntry #%s: %s", i, st); continue; @@ -40,13 +40,15 @@ export class Multicast { } async startup(enpoints: Array) { - await this._initialize(); - for (let ep of enpoints) { - if (!ep.id) continue; - for (let group_id of ep.member_of) { - await this.subscribe(group_id); + return this.driver.queue.execute(async () => { + await this._initialize(); + for (let ep of enpoints) { + if (!ep.id) continue; + for (let group_id of ep.member_of) { + await this.subscribe(group_id); + } } - } + }); } async subscribe(group_id: number):Promise { @@ -61,7 +63,7 @@ export class Multicast { entry.endpoint = 1; entry.multicastId = group_id; entry.networkIndex = 0; - const [status] = await this._ezsp.setMulticastTableEntry(idx, entry); + const [status] = await this.driver.ezsp.setMulticastTableEntry(idx, entry); if (status !== EmberStatus.SUCCESS) { this.logger( "Set MulticastTableEntry #%s for %s multicast id: %s", From ca69b1b7519383a80c017d59b17836193f031762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 30 Jan 2021 19:21:57 +0300 Subject: [PATCH 23/63] fix and logging --- src/adapter/ezsp/adapter/ezspAdapter.ts | 2 +- src/adapter/ezsp/driver/driver.ts | 32 ++++++++++++++----------- src/adapter/ezsp/driver/ezsp.ts | 2 +- src/adapter/ezsp/driver/multicast.ts | 21 +++++++++------- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 29c4157ac6..5dc6f5cf4f 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -108,7 +108,7 @@ class EZSPAdapter extends Adapter { stopBits: 1, xon: true, xoff: true - }, this.networkOptions, debug); + }, this.networkOptions); return Promise.resolve("resumed"); } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index ca2a7381c7..edad78750d 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -8,6 +8,12 @@ import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId } from './types/named'; import { Multicast } from './multicast'; import {Queue, Waitress, Wait} from '../../../utils'; +import Debug from "debug"; + +const debug = { + error: Debug('zigbee-herdsman:adapter:driver:error'), + log: Debug('zigbee-herdsman:adapter:driver:log'), +}; interface AddEndpointParameters { endpoint?: number, @@ -33,7 +39,6 @@ export class Driver extends EventEmitter { public networkParams: EmberNetworkParameters; private eui64ToNodeId = new Map(); private pending = new Map>>(); - private logger: any; private _nwk: EmberNodeId; private _ieee: EmberEUI64; private _multicast: Multicast; @@ -55,8 +60,7 @@ export class Driver extends EventEmitter { this.waitressValidator, this.waitressTimeoutFormatter); } - public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions, logger: any) { - this.logger = logger; + public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions) { this._nwkOpt = nwkOpt; let ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); @@ -80,7 +84,7 @@ export class Driver extends EventEmitter { await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); - this.logger("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); await this.addEndpoint({outputClusters: [0x0500]}); @@ -96,7 +100,7 @@ export class Driver extends EventEmitter { [patch, verInfo] = uint8_t.deserialize(uint8_t, verInfo); [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); const vers = `${major}.${minor}.${patch}.${special} build ${build}`; - this.logger(`EmberZNet version: ${vers}`); + debug.log(`EmberZNet version: ${vers}`); if (!await ezsp.networkInit()) { await this.form_network(); @@ -107,7 +111,7 @@ export class Driver extends EventEmitter { let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); console.assert(status == EmberStatus.SUCCESS); if (nodeType != EmberNodeType.COORDINATOR) { - this.logger(`Leaving current network as ${nodeType} and forming new network`); + debug.log(`Leaving current network as ${nodeType} and forming new network`); const [st] = await this.ezsp.leaveNetwork(); console.assert(st == EmberStatus.NETWORK_DOWN); await this.form_network(); @@ -115,7 +119,7 @@ export class Driver extends EventEmitter { console.assert(status == EmberStatus.SUCCESS); } this.networkParams = networkParams; - this.logger("Node type: %s, Network parameters: %s", nodeType, networkParams); + debug.log("Node type: %s, Network parameters: %s", nodeType, networkParams); await ezsp.updatePolicies(); @@ -123,12 +127,12 @@ export class Driver extends EventEmitter { this._nwk = nwk; const [ieee] = await this.ezsp.execCommand('getEui64'); this._ieee = ieee; - console.log('Network ready'); + debug.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); - this.logger(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); + debug.log(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); - this._multicast = new Multicast(this, logger); + this._multicast = new Multicast(this); await this._multicast.startup([]); } @@ -213,7 +217,7 @@ export class Driver extends EventEmitter { try { var arr = this.pending.get(tsn); if (!arr) { - console.log('Unexpected reponse TSN=', tsn, 'command=', commandId, args) + debug.log('Unexpected reponse TSN=', tsn, 'command=', commandId, args) return; }; let [sendDeferred, replyDeferred] = arr; @@ -223,7 +227,7 @@ export class Driver extends EventEmitter { replyDeferred.resolve(args); return; } catch (e) { - console.log(e); + debug.log(e); return; } } @@ -251,7 +255,7 @@ export class Driver extends EventEmitter { await this.ezsp.execCommand('setExtendedTimeout', eui64, true); let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); - console.log('unicast message sent, waiting for reply'); + debug.log('unicast message sent, waiting for reply'); return true; } catch (e) { return false; @@ -400,7 +404,7 @@ export class Driver extends EventEmitter { inputClusters, outputClusters, ); - this.logger("Ezsp adding endpoint: %s", res); + debug.log("Ezsp adding endpoint: %s", res); } public waitFor(address: number, clusterId: number, timeout: number = 30000) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 28dde4b10b..79904e36a2 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -276,7 +276,7 @@ export class Ezsp extends EventEmitter { // eslint-disable-next-line const self = this; - this.socketPort.on('ready', async function() { + this.socketPort.on('ready', async (error): Promise => { debug.log('Socket ready'); // reset await this.reset(); diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 38b7bf4dad..5f00a8cfa0 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -3,18 +3,21 @@ import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; import * as t from './types/basic'; import { EmberStatus, EmberOutgoingMessageType, EmberMulticastId } from './types/named'; import { EmberMulticastTableEntry } from './types/struct'; +import Debug from "debug"; + +const debug = { + log: Debug('zigbee-herdsman:adapter:ezsp:multicast'), +}; export class Multicast { TABLE_SIZE = 16; private driver: Driver; - private logger: any; private _multicast: any; private _available: Array; - constructor(driver: Driver, logger: any){ + constructor(driver: Driver){ this.driver = driver; - this.logger = logger; this._multicast = {}; this._available = []; } @@ -27,10 +30,10 @@ export class Multicast { let st: any, entry: any; [st, entry] = await this.driver.ezsp.getMulticastTableEntry(i); if (st !== EmberStatus.SUCCESS) { - this.logger("Couldn't get MulticastTableEntry #%s: %s", i, st); + debug.log("Couldn't get MulticastTableEntry #%s: %s", i, st); continue; } - this.logger("MulticastTableEntry[%s] = %s", i, entry); + debug.log("MulticastTableEntry[%s] = %s", i, entry); if (entry.endpoint !== 0) { this._multicast[entry.multicastId] = [entry, i]; } else { @@ -53,7 +56,7 @@ export class Multicast { async subscribe(group_id: number):Promise { if (this._multicast.indexOf(group_id) >= 0) { - this.logger("%s is already subscribed", group_id); + debug.log("%s is already subscribed", group_id); return EmberStatus.SUCCESS; } @@ -65,7 +68,7 @@ export class Multicast { entry.networkIndex = 0; const [status] = await this.driver.ezsp.setMulticastTableEntry(idx, entry); if (status !== EmberStatus.SUCCESS) { - this.logger( + debug.log( "Set MulticastTableEntry #%s for %s multicast id: %s", idx, entry.multicastId, @@ -76,7 +79,7 @@ export class Multicast { } this._multicast[entry.multicastId] = [entry, idx]; - this.logger( + debug.log( "Set MulticastTableEntry #%s for %s multicast id: %s", idx, entry.multicastId, @@ -84,7 +87,7 @@ export class Multicast { ) return status; } catch (e) { - this.logger("No more available slots MulticastId subscription"); + debug.log("No more available slots MulticastId subscription"); return EmberStatus.INDEX_OUT_OF_RANGE; } } From 58e28a0c5177845066d1dcd85855f259d5c5263f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 30 Jan 2021 23:01:21 +0300 Subject: [PATCH 24/63] active endpoints --- src/adapter/ezsp/adapter/ezspAdapter.ts | 65 +++++++++++++++++++++---- src/adapter/ezsp/driver/commands.ts | 4 +- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 5dc6f5cf4f..a1a088632d 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -60,11 +60,40 @@ class EZSPAdapter extends Adapter { if (!frame.senderEui64) { frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) } - if (frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { - const [nwk, rest] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); - const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); - debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); - this.handleDeviceJoin([nwk, ieee, 0]); + if (frame.apsFrame.profileId == 0) { + if ( + frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && + frame.apsFrame.destinationEndpoint == 0) { + let nwk, ieee; + [nwk, ieee] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + //const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); + debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); + this.handleDeviceJoin([nwk, ieee]); + } + } else if (frame.apsFrame.profileId == 260) { + try { + const payload: Events.ZclDataPayload = { + frame: ZclFrame.fromBuffer(frame.apsFrame.clusterId, frame.message), + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + //this.waitress.resolve(payload); + this.emit(Events.Events.zclData, payload); + } catch (error) { + const payload: Events.RawDataPayload = { + clusterID: frame.apsFrame.clusterId, + data: frame.message, + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + this.emit(Events.Events.rawData, payload); + } } this.emit('event', frame); } @@ -72,10 +101,10 @@ class EZSPAdapter extends Adapter { private async handleDeviceJoin(arr: any[]) { // todo let [nwk, ieee] = arr; - debug('Device join request received: %s %s', nwk, ieee); + debug('Device join request received: %s %s', nwk, ieee.toString('hex')); const payload: Events.DeviceJoinedPayload = { networkAddress: nwk, - ieeeAddr: ieee, + ieeeAddr: `0x${ieee.toString('hex')}`, }; if (nwk == 0) { @@ -191,8 +220,8 @@ class EZSPAdapter extends Adapter { frame.destinationEndpoint = 0; frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); await this.driver.request(networkAddress, frame, payload); const descriptor = await response.start().promise; debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); @@ -204,7 +233,25 @@ class EZSPAdapter extends Adapter { public async activeEndpoints(networkAddress: number): Promise { // todo - return Promise.reject(); + debug(`Requesting 'Active endpoints' for '${networkAddress}'`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Active_EP_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); + await this.driver.request(networkAddress, frame, payload); + const activeEp = await response.start().promise; + debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); + const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); + debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); + return {endpoints: [...message[3]]}; + }, networkAddress); } public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 9b5ffbf1d0..95c697083a 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -695,8 +695,10 @@ export const COMMANDS = { //// EmberZDOCmd export const ZDO_COMMANDS = { - "Node_Desc_req": [2, [uint8_t, EmberNodeId], [EmberStatus]], + "Node_Desc_req": [0x0002, [uint8_t, EmberNodeId], [EmberStatus]], "Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], + "Active_EP_req": [0x0005, [uint8_t, EmberNodeId], [EmberStatus]], + "Active_EP_rsp": [0x8005, [EmberStatus, uint8_t, EmberNodeId, LVBytes], []], } //# sourceMappingURL=commands.js.map From f5c1f2db90bf8ee1f8bdc8334bab40e32938ff85 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 31 Jan 2021 22:08:57 +0300 Subject: [PATCH 25/63] Source routing --- src/adapter/ezsp/driver/commands.ts | 1 + src/adapter/ezsp/driver/driver.ts | 3 +++ src/adapter/ezsp/driver/ezsp.ts | 24 ++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 95c697083a..8261248fc7 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -691,6 +691,7 @@ export const COMMANDS = { [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] ], "changeSourceRouteHandler": [196, [], [EmberNodeId, EmberNodeId]], //Bool + "setSourceRouteDiscoveryMode": [0x005A, [uint8_t,], [uint32_t,]], }; //// EmberZDOCmd diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index edad78750d..0b9964930b 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -83,6 +83,9 @@ export class Driver extends EventEmitter { await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + + await ezsp.setSourceRouting(); + const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 79904e36a2..6e24c960f4 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -3,7 +3,7 @@ import { Writer, Parser, FLAG, CANCEL } from './uart'; import { COMMANDS, ZDO_COMMANDS } from './commands'; import { Deferred, crc16ccitt } from './utils'; -import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask } from './types/named'; +import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; import SerialPort from 'serialport'; @@ -30,7 +30,10 @@ enum NcpResetCode { const RANDOMIZE_START = 0x42; const RANDOMIZE_SEQ = 0xB8; - +const MTOR_MIN_INTERVAL = 10; +const MTOR_MAX_INTERVAL = 90; +const MTOR_ROUTE_ERROR_THRESHOLD = 4; +const MTOR_DELIVERY_FAIL_THRESHOLD = 3; export class Ezsp extends EventEmitter { ezsp_version = 4; @@ -594,4 +597,21 @@ export class Ezsp extends EventEmitter { public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); } + + public async setSourceRouting() { + const [res] = await this.execCommand('setConcentrator', + true, + EmberConcentratorType.HIGH_RAM_CONCENTRATOR, + MTOR_MIN_INTERVAL, + MTOR_MAX_INTERVAL, + MTOR_ROUTE_ERROR_THRESHOLD, + MTOR_DELIVERY_FAIL_THRESHOLD, + 0, + ) + debug.log("Set concentrator type: %s", res); + if (res != EmberStatus.SUCCESS) { + debug.log("Couldn't set concentrator type %s: %s", true, res); + } + await this.execCommand('setSourceRouteDiscoveryMode', 1); + } } From 7603543edfcb1fec1c6b96d2e6ff047a44bd5f16 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Wed, 3 Feb 2021 22:23:17 +0300 Subject: [PATCH 26/63] work with form network --- src/adapter/ezsp/driver/driver.ts | 47 ++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 0b9964930b..b5e2fbc363 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -5,10 +5,11 @@ import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; import { EmberObject } from './types/emberObj'; import { Deferred, ember_security } from './utils'; -import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId } from './types/named'; +import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType } from './types/named'; import { Multicast } from './multicast'; import {Queue, Waitress, Wait} from '../../../utils'; import Debug from "debug"; +import equals from 'fast-deep-equal/es6'; const debug = { error: Debug('zigbee-herdsman:adapter:driver:error'), @@ -105,22 +106,21 @@ export class Driver extends EventEmitter { const vers = `${major}.${minor}.${patch}.${special} build ${build}`; debug.log(`EmberZNet version: ${vers}`); - if (!await ezsp.networkInit()) { + if (await this.needsToBeInitialised(nwkOpt)) { + const currentState = await ezsp.execCommand('networkState'); + console.log('Network state', currentState); + if (currentState == EmberNetworkStatus.JOINED_NETWORK) { + debug.log(`Leaving current network and forming new network`); + const st = await this.ezsp.leaveNetwork(); + console.assert(st == EmberStatus.NETWORK_DOWN); + } await this.form_network(); const state = await ezsp.execCommand('networkState'); - console.log('Network state', state); + debug.log('Network state', state); } let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); console.assert(status == EmberStatus.SUCCESS); - if (nodeType != EmberNodeType.COORDINATOR) { - debug.log(`Leaving current network as ${nodeType} and forming new network`); - const [st] = await this.ezsp.leaveNetwork(); - console.assert(st == EmberStatus.NETWORK_DOWN); - await this.form_network(); - [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); - console.assert(status == EmberStatus.SUCCESS); - } this.networkParams = networkParams; debug.log("Node type: %s, Network parameters: %s", nodeType, networkParams); @@ -139,16 +139,34 @@ export class Driver extends EventEmitter { await this._multicast.startup([]); } + + private async needsToBeInitialised(options: TsType.NetworkOptions): Promise { + let valid = true; + valid = valid && (await this.ezsp.networkInit()); + let [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); + debug.log("Current Node type: %s, Network parameters: %s", nodeType, networkParams); + valid = valid && (status == EmberStatus.SUCCESS); + valid = valid && (nodeType == EmberNodeType.COORDINATOR); + valid = valid && (options.panID == networkParams.panId); + valid = valid && (options.channelList.includes(networkParams.radioChannel)); + valid = valid && (equals(options.extendedPanID, networkParams.extendedPanId)); + return !valid; + } + private async form_network() { + let status; + [status] = await this.ezsp.execCommand('clearKeyTable'); + console.assert(status == EmberStatus.SUCCESS); + const panID = this._nwkOpt.panID; const extendedPanID = this._nwkOpt.extendedPanID; const hashed_tclk = this.ezsp.ezsp_version > 4; const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt, true, hashed_tclk); - const [status] = await this.ezsp.setInitialSecurityState(initial_security_state); + [status] = await this.ezsp.setInitialSecurityState(initial_security_state); const parameters:EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; parameters.extendedPanId = extendedPanID; - parameters.radioTxPower = 8; + parameters.radioTxPower = 20; parameters.radioChannel = this._nwkOpt.channelList[0]; parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; parameters.nwkManagerId = 0; @@ -157,6 +175,9 @@ export class Driver extends EventEmitter { await this.ezsp.formNetwork(parameters); await this.ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); + + await this.ezsp.execCommand('getKey', EmberKeyType.TRUST_CENTER_LINK_KEY); + await this.ezsp.execCommand('getKey', EmberKeyType.CURRENT_NETWORK_KEY); } private handleFrame(frameName: string, ...args: any[]) { From d922bb16e40b6f59ead24f051a93a8d585fcfa68 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 4 Feb 2021 23:26:18 +0300 Subject: [PATCH 27/63] additional configs --- src/adapter/ezsp/driver/driver.ts | 47 ++++++++++++------------ src/adapter/ezsp/driver/ezsp.ts | 50 +++++++++++++++++++++++++- src/adapter/ezsp/driver/types/named.ts | 4 +++ 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index b5e2fbc363..26aa2d8cee 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -66,32 +66,33 @@ export class Driver extends EventEmitter { let ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); const version = await ezsp.version(); - console.log('Got version', version); - - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS, - EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS - | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_SECURITY_LEVEL, 5); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 8); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_STACK_PROFILE, 2); - await ezsp.setConfigurationValue(EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 0xff); + // console.log('Got version', version); + + await ezsp.updateConfig(); + + await ezsp.updatePolicies(); + + await this.ezsp.setValue(EzspValueId.VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE, 82); + await this.ezsp.setValue(EzspValueId.VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE, 82); + await this.ezsp.setValue(EzspValueId.VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE, 3); await ezsp.setSourceRouting(); - const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); - debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + //const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); + //debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); + + //await this.addEndpoint({outputClusters: [0x0500]}); + await this.addEndpoint({ + inputClusters: [0x0000, 0x0003, 0x0006, 0x000A, 0x0019, 0x001A, 0x0300], + outputClusters: [0x0000, 0x0003, 0x0004, 0x0005, 0x0006, 0x0008, 0x0020, + 0x0300, 0x0400, 0x0402, 0x0405, 0x0406, 0x0500, 0x0B01, 0x0B03, + 0x0B04, 0x0702, 0x1000, 0xFC01, 0xFC02] + }); + await this.addEndpoint({ + endpoint: 242, profileId: 0xA10E, deviceId: 0x61, + outputClusters: [0x0021] + }); - await this.addEndpoint({outputClusters: [0x0500]}); - // getting MFG_STRING token const mfgName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_STRING); // getting MFG_BOARD_NAME token @@ -124,8 +125,6 @@ export class Driver extends EventEmitter { this.networkParams = networkParams; debug.log("Node type: %s, Network parameters: %s", nodeType, networkParams); - await ezsp.updatePolicies(); - const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; const [ieee] = await this.ezsp.execCommand('getEui64'); diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 6e24c960f4..cea8773011 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -3,7 +3,7 @@ import { Writer, Parser, FLAG, CANCEL } from './uart'; import { COMMANDS, ZDO_COMMANDS } from './commands'; import { Deferred, crc16ccitt } from './utils'; -import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType } from './types/named'; +import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; import SerialPort from 'serialport'; @@ -504,9 +504,57 @@ export class Ezsp extends EventEmitter { return value; } + async updateConfig() { + const config = [ + [EzspConfigId.CONFIG_FRAGMENT_DELAY_MS, 50], + [EzspConfigId.CONFIG_TX_POWER_MODE, 3], + [EzspConfigId.CONFIG_FRAGMENT_WINDOW_SIZE, 1], + //[EzspConfigId.CONFIG_BEACON_JITTER_DURATION, 0], + [EzspConfigId.CONFIG_NEIGHBOR_TABLE_SIZE, 16], + [EzspConfigId.CONFIG_ROUTE_TABLE_SIZE, 16], + [EzspConfigId.CONFIG_BINDING_TABLE_SIZE, 0], + [EzspConfigId.CONFIG_KEY_TABLE_SIZE, 12], + [EzspConfigId.CONFIG_ZLL_GROUP_ADDRESSES, 0], + [EzspConfigId.CONFIG_ZLL_RSSI_THRESHOLD, -40], + [EzspConfigId.CONFIG_TRANSIENT_KEY_TIMEOUT_S, 180], + [EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT, 15], + [EzspConfigId.CONFIG_BROADCAST_TABLE_SIZE, 15], + [EzspConfigId.CONFIG_MAX_HOPS, 30], + + [EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680], // 30000 + [EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16], // 61 + [EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16], + [EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16], // 8 + [EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2], + [EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1], + [EzspConfigId.CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S, 90], + [EzspConfigId.CONFIG_APPLICATION_ZDO_FLAGS, + EmberZdoConfigurationFlags.APP_RECEIVES_SUPPORTED_ZDO_REQUESTS + | EmberZdoConfigurationFlags.APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS], + [EzspConfigId.CONFIG_SECURITY_LEVEL, 5], + [EzspConfigId.CONFIG_END_DEVICE_POLL_TIMEOUT, 8], // 14 + [EzspConfigId.CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD, 2], + [EzspConfigId.CONFIG_MAX_END_DEVICE_CHILDREN, 32], + [EzspConfigId.CONFIG_STACK_PROFILE, 2], + [EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 255], + ]; + + for (let [confName, value] of config) { + await this.setConfigurationValue(confName, value); + } + } + async updatePolicies() { // Set up the policies for what the NCP should do. const policies = [ + [EzspPolicyId.BINDING_MODIFICATION_POLICY, EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS], + [EzspPolicyId.UNICAST_REPLIES_POLICY, EzspDecisionId.HOST_WILL_NOT_SUPPLY_REPLY], + [EzspPolicyId.POLL_HANDLER_POLICY, EzspDecisionId.POLL_HANDLER_IGNORE], + [EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK], + [EzspPolicyId.PACKET_VALIDATE_LIBRARY_POLICY, EzspDecisionId.PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED], + [EzspPolicyId.ZLL_POLICY, EzspDecisionId.ALLOW_JOINS], + [EzspPolicyId.TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY, EzspDecisionId.ALLOW_JOINS], + [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 5a547835a6..0ef79a9a4f 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -376,6 +376,8 @@ export class EzspValueId extends basic.uint8_t { static VALUE_PTA_OPTIONS = 0x32 // Configure manufacturing library options(0-non-CSMA transmits,1-CSMA transmits). static VALUE_MFGLIB_OPTIONS = 0x33 + + static VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F; } export class EzspExtendedValueId extends basic.uint8_t { @@ -449,6 +451,8 @@ export class EzspPolicyId extends basic.uint8_t { static PACKET_VALIDATE_LIBRARY_POLICY = 0x07 // Controls whether the stack will process ZLL messages. static ZLL_POLICY = 0x08 + + static TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 } From 47668e9e1359287665a3dce5e4651a7e82366dba Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 12 Feb 2021 12:00:33 +0300 Subject: [PATCH 28/63] fix --- src/adapter/ezsp/driver/ezsp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index cea8773011..dd03a20d57 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -515,7 +515,7 @@ export class Ezsp extends EventEmitter { [EzspConfigId.CONFIG_BINDING_TABLE_SIZE, 0], [EzspConfigId.CONFIG_KEY_TABLE_SIZE, 12], [EzspConfigId.CONFIG_ZLL_GROUP_ADDRESSES, 0], - [EzspConfigId.CONFIG_ZLL_RSSI_THRESHOLD, -40], + [EzspConfigId.CONFIG_ZLL_RSSI_THRESHOLD, 215], // -40 [EzspConfigId.CONFIG_TRANSIENT_KEY_TIMEOUT_S, 180], [EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT, 15], [EzspConfigId.CONFIG_BROADCAST_TABLE_SIZE, 15], From 7d61ed0c82ffb26c457a79b2554496375c025a6a Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 18 Feb 2021 13:22:56 +0300 Subject: [PATCH 29/63] isolated serialDriver --- src/adapter/ezsp/driver/ezsp.ts | 352 ++----------------------------- src/adapter/ezsp/driver/uart.ts | 353 +++++++++++++++++++++++++++++++- 2 files changed, 363 insertions(+), 342 deletions(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index dd03a20d57..263a2f0097 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,14 +1,11 @@ import * as t from './types'; -import { Writer, Parser, FLAG, CANCEL } from './uart'; +import { SerialDriver } from './uart'; import { COMMANDS, ZDO_COMMANDS } from './commands'; -import { Deferred, crc16ccitt } from './utils'; +import { Deferred } from './utils'; import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; -import SerialPort from 'serialport'; -import net from 'net'; -import SocketPortUtils from '../../socketPortUtils'; import Debug from "debug"; const debug = { @@ -16,20 +13,7 @@ const debug = { log: Debug('zigbee-herdsman:adapter:ezsp:log'), }; -enum NcpResetCode { - RESET_UNKNOWN_REASON = 0x00, - RESET_EXTERNAL = 0x01, - RESET_POWER_ON = 0x02, - RESET_WATCHDOG = 0x03, - RESET_ASSERT = 0x06, - RESET_BOOTLOADER = 0x09, - RESET_SOFTWARE = 0x0B, - ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, - ERROR_UNKNOWN_EM3XX_ERROR = 0x80, -} -const RANDOMIZE_START = 0x42; -const RANDOMIZE_SEQ = 0xB8; const MTOR_MIN_INTERVAL = 10; const MTOR_MAX_INTERVAL = 90; const MTOR_ROUTE_ERROR_THRESHOLD = 4; @@ -37,332 +21,33 @@ const MTOR_DELIVERY_FAIL_THRESHOLD = 3; export class Ezsp extends EventEmitter { ezsp_version = 4; - _seq = 0; - send_seq = 0; - recv_seq = 0; - _tc_policy: any; + // command sequence + cmdSeq = 0; _awaiting = new Map }>(); COMMANDS_BY_ID = new Map(); _cbCounter = 0; - reset_deferred: Deferred; - private portType: 'serial' | 'socket'; - private serialPort: SerialPort; - private socketPort: net.Socket; - private writer: Writer; - private parser: Parser; - private initialized: boolean; + private serialDriver: SerialDriver; constructor() { super(); - this.initialized = false; for (let name in COMMANDS) { let details = (COMMANDS)[name]; this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); } - - this.onParsed = this.onParsed.bind(this); - this.onPortClose = this.onPortClose.bind(this); - } - - //////////////////////// serial routines //////////////////////// - - private onParsed(data: Buffer): void { - try { - // const object = ZpiObject.fromUnpiFrame(frame); - // const message = - // `<-- ${Subsystem[object.subsystem]} - ${object.command} - ${JSON.stringify(object.payload)}`; - // this.log(object.type, message); - // this.waitress.resolve(object); - // this.emit('received', object); - //debug.log(`<===== frame : ${data.toString('hex')}`); - /* Frame receive handler */ - switch (true) { - case ((data[0] & 128) === 0): - debug.log("DATA frame: %s", data.toString('hex')); - this.data_frame_received(data); - break; - - case ((data[0] & 224) === 128): - debug.log("ACK frame: %s", data.toString('hex')); - this.handle_ack(data[0]); - break; - - case ((data[0] & 224) === 160): - debug.log("NAK frame: %s", data.toString('hex')); - this.handle_nak(data[0]); - break; - - case (data[0] === 192): - debug.log("RST frame: %s", data.toString('hex')); - break; - - case (data[0] === 193): - debug.log("RSTACK frame: %s", data.toString('hex')); - this.rstack_frame_received(data); - break; - - case (data[0] === 194): - debug.log("Error frame:", data.toString('hex')); - break; - default: - debug.error("UNKNOWN FRAME RECEIVED: %r", data); - } - - } catch (error) { - debug.error(`Error while parsing to ZpiObject '${error.stack}'`); - } - } - - private data_frame_received(data: Buffer) { - /* Data frame receive handler */ - var seq; - seq = ((data[0] & 112) >> 4); - this.recv_seq = ((seq + 1) % 8); - debug.log('send ACK'); - this.writer.writeBuffer(this.make_ack_frame()); - this.handle_ack(data[0]); - data = data.slice(1, (- 3)); - //this.waitress.resolve(data); - //this.emit('received', this.randomize(data)); - const frame = this.randomize(data); - this.frame_received(frame); - } - - private handle_ack(control: number) { - /* Handle an acknowledgement frame */ - // var ack, pending; - // ack = (((control & 7) - 1) % 8); - // if ((ack === this._pending[0])) { - // [pending, this._pending] = [this._pending, [(- 1), null]]; - // pending[1].set_result(true); - // } - } - - private handle_nak(control: number) { - /* Handle negative acknowledgment frame */ - // let nak = (control & 7); - // if ((nak === this._pending[0])) { - // this._pending[1].set_result(false); - // } - } - - private rstack_frame_received(data: Buffer) { - /* Reset acknowledgement frame receive handler */ - var code; - this.send_seq = 0; - this.recv_seq = 0; - try { - code = NcpResetCode[data[2]]; - } catch (e) { - code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; - } - debug.log("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); - if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { - return; - } - if ((!this.reset_deferred)) { - debug.log("Reset future is None"); - return; - } - this.reset_deferred.resolve(true); - } - - private make_ack_frame(): Buffer { - /* Construct a acknowledgement frame */ - console.assert(((0 <= this.recv_seq) && (this.recv_seq < 8))); - return this.make_frame([(0b10000000 | (this.recv_seq & 0b00000111))]); - } - - private make_frame(control: ArrayLike, data?: ArrayLike): Buffer { - /* Construct a frame */ - const ctrlArr: Array = Array.from(control); - const dataArr: Array = (data && Array.from(data)) || []; - - const sum = ctrlArr.concat(dataArr); - - let crc = crc16ccitt(Buffer.from(sum), 65535); - let crcArr = [(crc >> 8), (crc % 256)]; - return Buffer.concat([this.writer.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); - } - - private randomize(s: Buffer): Buffer { - /*XOR s with a pseudo-random sequence for transmission - Used only in data frames - */ - let rand = RANDOMIZE_START; - let out = Buffer.alloc(s.length); - let outIdx = 0; - for (let c of s){ - out.writeUInt8(c ^ rand, outIdx++); - if ((rand % 2)) { - rand = ((rand >> 1) ^ RANDOMIZE_SEQ); - } else { - rand = (rand >> 1); - } - } - return out; - } - - public isInitialized(): boolean { - return this.initialized; - } - - private onPortClose(): void { - debug.log('Port closed'); - this.initialized = false; - this.emit('close'); - } - - async connect(path: string, options: {}) { - this.portType = SocketPortUtils.isTcpPath(path) ? 'socket' : 'serial'; - this.portType === 'serial' ? await this.openSerialPort(path, options) : await this.openSocketPort(path, options); - } - - private async openSerialPort(path: string, opt: {}): Promise { - // @ts-ignore - const options = {baudRate: opt.baudRate, rtscts: false, autoOpen: false}; - - debug.log(`Opening SerialPort with ${path} and ${JSON.stringify(options)}`); - this.serialPort = new SerialPort(path, options); - - this.writer = new Writer(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this.writer.pipe(this.serialPort); - - this.parser = new Parser(); - this.serialPort.pipe(this.parser); - this.parser.on('parsed', this.onParsed); - - return new Promise((resolve, reject): void => { - this.serialPort.open(async (error): Promise => { - if (error) { - reject(new Error(`Error while opening serialport '${error}'`)); - this.initialized = false; - if (this.serialPort.isOpen) { - this.serialPort.close(); - } - } else { - debug.log('Serialport opened'); - this.serialPort.once('close', this.onPortClose); - this.serialPort.once('error', (error) => { - debug.error(`Serialport error: ${error}`); - }); - // reset - await this.reset(); - this.initialized = true; - resolve(); - } - }); - }); - } - - private async openSocketPort(path: string, options: {}): Promise { - const info = SocketPortUtils.parseTcpPath(path); - debug.log(`Opening TCP socket with ${info.host}:${info.port}`); - - this.socketPort = new net.Socket(); - this.socketPort.setNoDelay(true); - this.socketPort.setKeepAlive(true, 15000); - - this.writer = new Writer(); - this.writer.pipe(this.socketPort); - - this.parser = new Parser(); - this.socketPort.pipe(this.parser); - this.parser.on('parsed', this.onParsed); - - return new Promise((resolve, reject): void => { - this.socketPort.on('connect', function() { - debug.log('Socket connected'); - }); - - // eslint-disable-next-line - const self = this; - this.socketPort.on('ready', async (error): Promise => { - debug.log('Socket ready'); - // reset - await this.reset(); - self.initialized = true; - resolve(); - }); - - this.socketPort.once('close', this.onPortClose); - - this.socketPort.on('error', function () { - debug.log('Socket error'); - reject(new Error(`Error while opening socket`)); - self.initialized = false; - }); - - this.socketPort.connect(info.port, info.host); - }); - } - - private write(data: Buffer) { - debug.log("write data: %s", data.toString('hex')); - let seq = this.send_seq; - this.send_seq = ((seq + 1) % 8); - let pack; - try { - pack = this.data_frame(data, seq, 0); - this.writer.writeBuffer(pack); - } catch (e) { - pack = this.data_frame(data, seq, 1); - this.writer.writeBuffer(pack); - } + + this.serialDriver = new SerialDriver(); + this.serialDriver.on('received', this.onFrameReceived.bind(this)); } - private data_frame(data: Buffer, seq: number, rxmit: number) { - /* Construct a data frame */ - let control; - console.assert(((0 <= seq) && (seq <= 7))); - console.assert(((0 <= rxmit) && (rxmit <= 1))); - control = (((seq << 4) | (rxmit << 3)) | this.recv_seq); - return this.make_frame([control], this.randomize(data)); - } - - async reset() { - // return this._gw.reset(); - debug.log('uart reseting'); - if ((this.reset_deferred)) { - throw new TypeError("reset can only be called on a new connection"); - } - /* Construct a reset frame */ - const rst_frame = Buffer.concat([Buffer.from([CANCEL]), this.make_frame([0xC0])]); - //this.write(rst_frame); - this.writer.writeBuffer(rst_frame); - this.reset_deferred = new Deferred(); - return this.reset_deferred.promise; + public async connect(path: string, options: {}) { + await this.serialDriver.connect(path, options); } public close(): Promise { - return new Promise((resolve, reject): void => { - if (this.initialized) { - if (this.portType === 'serial') { - this.serialPort.flush((): void => { - this.serialPort.close((error): void => { - this.initialized = false; - error == null ? - resolve() : - reject(new Error(`Error while closing serialport '${error}'`)); - this.emit('close'); - }); - }); - } else { - this.socketPort.destroy(); - resolve(); - } - } else { - resolve(); - this.emit('close'); - } - }); + return this.serialDriver.close(); } - //////////////////////// command routines //////////////////////// - - private frame_received(data: Buffer) { + private onFrameReceived(data: Buffer) { /*Handle a received EZSP frame The protocol has taken care of UART specific framing etc, so we should @@ -577,7 +262,7 @@ export class Ezsp extends EventEmitter { var c, data, frame, cmd_id; c = (COMMANDS)[name]; data = t.serialize(args, c[1]); - frame = [(this._seq & 255)]; + frame = [(this.cmdSeq & 255)]; if ((this.ezsp_version < 8)) { if ((this.ezsp_version >= 5)) { frame.push(0x00, 0xFF, 0x00, c[0]); @@ -593,15 +278,14 @@ export class Ezsp extends EventEmitter { private _command(name: string, ...args: any[]): Promise { var c, data, deferred; - debug.log(`---> Send command ${name}: (${args})`); + debug.log(`===> Send command ${name}: (${args})`); data = this._ezsp_frame(name, ...args); - debug.log(`---> Send data ${name}: (${data.toString('hex')})`); - //this._gw.data(data); - this.write(data); + debug.log(`===> Send data ${name}: (${data.toString('hex')})`); + this.serialDriver.send(data); c = (COMMANDS)[name]; deferred = new Deferred(); - this._awaiting.set(this._seq, { expectedId: c[0], schema: c[2], deferred }); - this._seq = (this._seq + 1 % 256); + this._awaiting.set(this.cmdSeq, { expectedId: c[0], schema: c[2], deferred }); + this.cmdSeq = (this.cmdSeq + 1 % 256); return deferred.promise; } diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 3782056a79..2366d18d44 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -1,19 +1,40 @@ +import { EventEmitter } from 'events'; +import SerialPort from 'serialport'; +import net from 'net'; +import SocketPortUtils from '../../socketPortUtils'; +import { Deferred, crc16ccitt } from './utils'; import * as stream from 'stream'; import Debug from "debug"; const debug = Debug('zigbee-herdsman:adapter:ezsp:uart'); -export const FLAG = 0x7E // Marks end of frame -export const ESCAPE = 0x7D -export const CANCEL = 0x1A // Terminates a frame in progress + +const FLAG = 0x7E // Marks end of frame +const ESCAPE = 0x7D // Indicates that the following byte is escaped +const CANCEL = 0x1A // Terminates a frame in progress const XON = 0x11 // Resume transmission const XOFF = 0x13 // Stop transmission -const SUBSTITUTE = 0x18 +const SUBSTITUTE = 0x18 // Replaces a byte received with a low-level communication error const STUFF = 0x20 -export const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] +const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] +const RANDOMIZE_START = 0x42; +const RANDOMIZE_SEQ = 0xB8; + + +enum NcpResetCode { + RESET_UNKNOWN_REASON = 0x00, + RESET_EXTERNAL = 0x01, + RESET_POWER_ON = 0x02, + RESET_WATCHDOG = 0x03, + RESET_ASSERT = 0x06, + RESET_BOOTLOADER = 0x09, + RESET_SOFTWARE = 0x0B, + ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51, + ERROR_UNKNOWN_EM3XX_ERROR = 0x80, +} -export class Parser extends stream.Transform { +class Parser extends stream.Transform { private buffer: Buffer; public constructor() { @@ -42,7 +63,6 @@ export class Parser extends stream.Transform { debug(`<-- [${this.buffer.toString('hex')}] [${[...this.buffer]}]`); try { const frame = this.extract_frame(); - debug(`<-- parsed ${frame.toString('hex')}`); if (frame) { this.emit('parsed', frame); } @@ -88,7 +108,7 @@ export class Parser extends stream.Transform { } } -export class Writer extends stream.Readable { +class Writer extends stream.Readable { public writeBuffer(buffer: Buffer): void { debug(`--> [${buffer.toString('hex')}] [${[...buffer]}]`); this.push(buffer); @@ -110,4 +130,321 @@ export class Writer extends stream.Readable { } return out.slice(0, outIdx); } +} + + +export class SerialDriver extends EventEmitter { + private serialPort: SerialPort; + private socketPort: net.Socket; + private writer: Writer; + private parser: Parser; + private initialized: boolean; + private resetDeferred: Deferred; + private portType: 'serial' | 'socket'; + private sendSeq: number = 0; // next frame number to send + private recvSeq: number = 0; // next frame number to receive + private ackSeq: number = 0; // next number after the last accepted frame + + constructor(){ + super(); + this.initialized = false; + } + + async connect(path: string, options: {}) { + this.portType = SocketPortUtils.isTcpPath(path) ? 'socket' : 'serial'; + if (this.portType === 'serial') { + await this.openSerialPort(path, options); + } else { + await this.openSocketPort(path, options); + } + } + + private async openSerialPort(path: string, opt: {}): Promise { + // @ts-ignore + const options = {baudRate: opt.baudRate, rtscts: false, autoOpen: false}; + + debug(`Opening SerialPort with ${path} and ${JSON.stringify(options)}`); + this.serialPort = new SerialPort(path, options); + + this.writer = new Writer(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.writer.pipe(this.serialPort); + + this.parser = new Parser(); + this.serialPort.pipe(this.parser); + this.parser.on('parsed', this.onParsed.bind(this)); + + return new Promise((resolve, reject): void => { + this.serialPort.open(async (error): Promise => { + if (error) { + reject(new Error(`Error while opening serialport '${error}'`)); + this.initialized = false; + if (this.serialPort.isOpen) { + this.serialPort.close(); + } + } else { + debug('Serialport opened'); + this.serialPort.once('close', this.onPortClose.bind(this)); + this.serialPort.once('error', (error) => { + debug(`Serialport error: ${error}`); + }); + // reset + await this.reset(); + this.initialized = true; + resolve(); + } + }); + }); + } + + private async openSocketPort(path: string, options: {}): Promise { + const info = SocketPortUtils.parseTcpPath(path); + debug(`Opening TCP socket with ${info.host}:${info.port}`); + + this.socketPort = new net.Socket(); + this.socketPort.setNoDelay(true); + this.socketPort.setKeepAlive(true, 15000); + + this.writer = new Writer(); + this.writer.pipe(this.socketPort); + + this.parser = new Parser(); + this.socketPort.pipe(this.parser); + this.parser.on('parsed', this.onParsed.bind(this)); + + return new Promise((resolve, reject): void => { + this.socketPort.on('connect', function() { + debug('Socket connected'); + }); + + // eslint-disable-next-line + const self = this; + this.socketPort.on('ready', async (error): Promise => { + debug('Socket ready'); + // reset + await this.reset(); + self.initialized = true; + resolve(); + }); + + this.socketPort.once('close', this.onPortClose.bind(this)); + + this.socketPort.on('error', function () { + debug('Socket error'); + reject(new Error(`Error while opening socket`)); + self.initialized = false; + }); + + this.socketPort.connect(info.port, info.host); + }); + } + + // private showCounters(frmNum?: number, ackNum?: number) { + // //debug.log(`send [${this.sendSeq}:${(frmNum==undefined) ? ' ' : frmNum}][${this.recvSeq}:${(ackNum==undefined) ? ' ' : ackNum}] recv `); + // debug(`[${this.sendSeq}:${this.ackSeq}|${this.recvSeq}]`); + // } + + private onParsed(data: Buffer): void { + try { + /* Frame receive handler */ + switch (true) { + case ((data[0] & 0x80) === 0): + debug(`Data frame : ${data.toString('hex')}`); + this.data_frame_received(data); + break; + + case ((data[0] & 0xE0) === 0x80): + debug(`ACK frame : ${data.toString('hex')}`); + this.handle_ack(data[0]); + break; + + case ((data[0] & 0xE0) === 0xA0): + debug(`NAK frame : ${data.toString('hex')}`); + this.handle_nak(data[0]); + break; + + case (data[0] === 0xC0): + debug(`RST frame : ${data.toString('hex')}`); + break; + + case (data[0] === 0xC1): + debug(`RSTACK frame: ${data.toString('hex')}`); + this.rstack_frame_received(data); + break; + + case (data[0] === 0xC2): + debug(`Error frame : ${data.toString('hex')}`); + break; + default: + debug("UNKNOWN FRAME RECEIVED: %r", data); + } + + } catch (error) { + debug(`Error while parsing to ZpiObject '${error.stack}'`); + } + } + + private data_frame_received(data: Buffer) { + /* Data frame receive handler */ + const seq = ((data[0] & 0x70) >> 4); + // if (seq !== this.recvSeq) { + // debug('NAK-NAK'); + // } + this.recvSeq = (seq + 1) & 7; // next + const ack_frame = this.make_ack_frame(); + this.handle_ack(data[0]); + + debug(`Write ack`); + this.writer.writeBuffer(ack_frame); + + data = data.slice(1, (- 3)); + const frame = this.randomize(data); + this.emit('received', frame); + } + + private handle_ack(control: number) { + /* Handle an acknowledgement frame */ + // next number after the last accepted frame + this.ackSeq = control & 7; + // var ack, pending; + // ack = (((control & 7) - 1) % 8); + // if ((ack === this._pending[0])) { + // [pending, this._pending] = [this._pending, [(- 1), null]]; + // pending[1].set_result(true); + // } + } + + private handle_nak(control: number) { + /* Handle negative acknowledgment frame */ + // let nak = (control & 7); + // if ((nak === this._pending[0])) { + // this._pending[1].set_result(false); + // } + } + + private rstack_frame_received(data: Buffer) { + /* Reset acknowledgement frame receive handler */ + var code; + this.sendSeq = 0; + this.recvSeq = 0; + try { + code = NcpResetCode[data[2]]; + } catch (e) { + code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; + } + debug("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { + return; + } + if ((!this.resetDeferred)) { + debug("Reset future is None"); + return; + } + this.resetDeferred.resolve(true); + } + + private make_ack_frame(): Buffer { + /* Construct a acknowledgement frame */ + return this.make_frame([(0b10000000 | this.recvSeq)]); + } + + private make_frame(control: ArrayLike, data?: ArrayLike): Buffer { + /* Construct a frame */ + const ctrlArr: Array = Array.from(control); + const dataArr: Array = (data && Array.from(data)) || []; + + const sum = ctrlArr.concat(dataArr); + + let crc = crc16ccitt(Buffer.from(sum), 65535); + let crcArr = [(crc >> 8), (crc % 256)]; + return Buffer.concat([this.writer.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); + } + + private randomize(s: Buffer): Buffer { + /*XOR s with a pseudo-random sequence for transmission + Used only in data frames + */ + let rand = RANDOMIZE_START; + let out = Buffer.alloc(s.length); + let outIdx = 0; + for (let c of s){ + out.writeUInt8(c ^ rand, outIdx++); + if ((rand % 2)) { + rand = ((rand >> 1) ^ RANDOMIZE_SEQ); + } else { + rand = (rand >> 1); + } + } + return out; + } + + private data_frame(data: Buffer, seq: number, rxmit: number) { + /* Construct a data frame */ + const control = (((seq << 4) | (rxmit << 3)) | this.recvSeq); + return this.make_frame([control], this.randomize(data)); + } + + async reset() { + // return this._gw.reset(); + debug('uart reseting'); + if ((this.resetDeferred)) { + throw new TypeError("reset can only be called on a new connection"); + } + /* Construct a reset frame */ + const rst_frame = Buffer.concat([Buffer.from([CANCEL]), this.make_frame([0xC0])]); + debug(`Write reset`); + this.writer.writeBuffer(rst_frame); + this.resetDeferred = new Deferred(); + return this.resetDeferred.promise; + } + + public close(): Promise { + return new Promise((resolve, reject): void => { + if (this.initialized) { + if (this.portType === 'serial') { + this.serialPort.flush((): void => { + this.serialPort.close((error): void => { + this.initialized = false; + error == null ? + resolve() : + reject(new Error(`Error while closing serialport '${error}'`)); + this.emit('close'); + }); + }); + } else { + this.socketPort.destroy(); + resolve(); + } + } else { + resolve(); + this.emit('close'); + } + }); + } + + private onPortClose(): void { + debug('Port closed'); + this.initialized = false; + this.emit('close'); + } + + public isInitialized(): boolean { + return this.initialized; + } + + + public send(data: Buffer) { + let seq = this.sendSeq; + this.sendSeq = ((seq + 1) % 8); // next + debug(`Write frame (${seq}): ${data.toString('hex')}`); + let pack; + try { + pack = this.data_frame(data, seq, 0); + this.writer.writeBuffer(pack); + } catch (e) { + pack = this.data_frame(data, seq, 1); + this.writer.writeBuffer(pack); + } + } } \ No newline at end of file From b788f4ac4533045c64eba8782c29620487aa2eb5 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 18 Feb 2021 14:30:53 +0300 Subject: [PATCH 30/63] fix security issue some additional logs --- src/adapter/ezsp/driver/driver.ts | 6 ++---- src/adapter/ezsp/driver/ezsp.ts | 19 +++++++++++++++---- src/adapter/ezsp/driver/types/basic.ts | 10 ++++++++++ src/adapter/ezsp/driver/utils/index.ts | 18 +++++------------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 26aa2d8cee..5847f33d1a 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -159,8 +159,7 @@ export class Driver extends EventEmitter { const panID = this._nwkOpt.panID; const extendedPanID = this._nwkOpt.extendedPanID; - const hashed_tclk = this.ezsp.ezsp_version > 4; - const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt, true, hashed_tclk); + const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt); [status] = await this.ezsp.setInitialSecurityState(initial_security_state); const parameters:EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; @@ -403,8 +402,7 @@ export class Driver extends EventEmitter { } public async permitJoining(seconds:number){ - const [status] = await this.ezsp.execCommand('setPolicy', EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); - console.assert(status == EmberStatus.SUCCESS); + await this.ezsp.setPolicy(EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); return await this.ezsp.execCommand('permitJoining', seconds); } diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 263a2f0097..f3752aea63 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -134,16 +134,17 @@ export class Ezsp extends EventEmitter { async setConfigurationValue(configId: number, value: any) { let ret; + debug.log('Set %s = %s', EzspConfigId.valueToName(EzspConfigId, configId), value); [ret] = await this.execCommand('setConfigurationValue', configId, value); console.assert(ret === EmberStatus.SUCCESS); - debug.log('Set %s = %s', configId, value); } async getConfigurationValue(configId: number) { let ret, value; + debug.log('Get %s', EzspConfigId.valueToName(EzspConfigId, configId)); [ret, value] = await this.execCommand('getConfigurationValue', configId); console.assert(ret === EmberStatus.SUCCESS); - debug.log('Get %s = %s', configId, value); + debug.log('Got %s = %s', EzspConfigId.valueToName(EzspConfigId, configId), value); return value; } @@ -177,6 +178,7 @@ export class Ezsp extends EventEmitter { async setValue(valueId: t.EzspValueId, value: any) { let ret; + debug.log('Set %s = %s', t.EzspValueId.valueToName(t.EzspValueId, valueId), value); [ret] = await this.execCommand('setValue', valueId, value); console.assert(ret === EmberStatus.SUCCESS); return [ret]; @@ -184,11 +186,21 @@ export class Ezsp extends EventEmitter { async getValue(valueId: t.EzspValueId) { let ret, value; + debug.log('Get %s', t.EzspValueId.valueToName(t.EzspValueId, valueId)); [ret, value] = await this.execCommand('getValue', valueId); console.assert(ret === EmberStatus.SUCCESS); + debug.log('Got %s = %s', t.EzspValueId.valueToName(t.EzspValueId, valueId), value); return value; } + async setPolicy(policyId: EzspPolicyId, value: any) { + let ret; + debug.log('Set %s = %s', EzspPolicyId.valueToName(EzspPolicyId, policyId), value); + [ret] = await this.execCommand('setPolicy', policyId, value); + console.assert(ret === EmberStatus.SUCCESS); + return [ret]; + } + async updateConfig() { const config = [ [EzspConfigId.CONFIG_FRAGMENT_DELAY_MS, 50], @@ -246,8 +258,7 @@ export class Ezsp extends EventEmitter { ]; for (let [policy, value] of policies) { - const [status] = await this.execCommand('setPolicy', policy, value); - console.assert(status == EmberStatus.SUCCESS); + await this.setPolicy(policy, value); } } diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index 1d9444ad29..5dd8ca3a45 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -14,6 +14,16 @@ export class int_t { static deserialize(cls: any, data: Buffer) { return [cls._signed ? data.readIntLE(0, cls._size) : data.readUIntLE(0, cls._size), data.slice(cls._size)] } + + static valueToName(cls: any, value: any) { + for (let prop of Object.getOwnPropertyNames(cls)) { + const desc = Object.getOwnPropertyDescriptor(cls, prop); + if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { + return `${cls.name}.${prop}`; + } + }; + return ''; + } } export class int8s extends int_t { diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index d85d3bc75c..0c3af7108c 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -134,26 +134,18 @@ export class AsyncQueue { } -function ember_security(config: any, controller: boolean = false, hashed_tclk: boolean = true):EmberInitialSecurityState { +function ember_security(config: any):EmberInitialSecurityState { const isc: EmberInitialSecurityState = new EmberInitialSecurityState(); - isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | EmberInitialSecurityBitmask.REQUIRE_ENCRYPTED_KEY); + isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | + EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | + EmberInitialSecurityBitmask.HAVE_NETWORK_KEY | + EmberInitialSecurityBitmask.PRECONFIGURED_NETWORK_KEY_MODE); isc.preconfiguredKey = new EmberKeyData(); isc.preconfiguredKey.contents = Buffer.from("ZigBeeAlliance09"); isc.networkKey = new EmberKeyData(); isc.networkKey.contents = config.networkKey; isc.networkKeySequenceNumber = 0; isc.preconfiguredTrustCenterEui64 = new EmberEUI64([0, 0, 0, 0, 0, 0, 0, 0]); - - if (controller) { - isc.bitmask |= ( - EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | EmberInitialSecurityBitmask.HAVE_NETWORK_KEY - ) - if (hashed_tclk) { - isc.preconfiguredKey = new EmberKeyData(); - isc.preconfiguredKey.contents = Buffer.from("ZigBeeAlliance09"); - isc.bitmask |= EmberInitialSecurityBitmask.TRUST_CENTER_USES_HASHED_LINK_KEY; - } - } return isc; } From 6d783d9e89a7a99557a9eaaa322061749e7855f4 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 18 Feb 2021 17:59:19 +0300 Subject: [PATCH 31/63] fix ieee in device annce --- src/adapter/ezsp/adapter/ezspAdapter.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index a1a088632d..95044f6b62 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -64,10 +64,11 @@ class EZSPAdapter extends Adapter { if ( frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { - let nwk, ieee; - [nwk, ieee] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); - //const [ieee] = EmberEUI64.deserialize(EmberEUI64, rest as Buffer); - debug("ZDO Device announce: 0x%04x, %s", nwk, ieee); + let nwk, rst, ieee; + [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + [ieee, rst] = EmberEUI64.deserialize(EmberEUI64, rst as Buffer); + ieee = new EmberEUI64(ieee); + debug("ZDO Device announce: %s, %s", nwk, ieee.toString()); this.handleDeviceJoin([nwk, ieee]); } } else if (frame.apsFrame.profileId == 260) { From a9bdca6057493a47134dbbfecd707501b66badcb Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 18 Feb 2021 21:29:36 +0300 Subject: [PATCH 32/63] try to get Simple Descriptor... but not work yet --- src/adapter/ezsp/adapter/ezspAdapter.ts | 26 ++++++++++++++++++++++++- src/adapter/ezsp/driver/commands.ts | 6 ++++-- src/adapter/ezsp/driver/types/index.ts | 4 ++-- src/adapter/ezsp/driver/types/struct.ts | 12 ++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 95044f6b62..921d581296 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -257,7 +257,31 @@ class EZSPAdapter extends Adapter { public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { // todo - return Promise.reject(); + debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Simple_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = endpointID; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug(`activeEndpoints got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); + const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); + debug(`activeEndpoints got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); + return { + profileID: descriptor.payload.profileid, + endpointID: descriptor.payload.endpoint, + deviceID: descriptor.sender, + inputClusters: descriptor.payload.inclusterlist, + outputClusters: descriptor.payload.outclusterlist, + }; + }, networkAddress); } public waitFor( diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 8261248fc7..f1a69c7b08 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -26,7 +26,7 @@ import {/* Basic Types */ EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, + EmberNodeDescriptor, EmberSimpleDescriptor } from './types'; export const COMMANDS = { @@ -697,7 +697,9 @@ export const COMMANDS = { //// EmberZDOCmd export const ZDO_COMMANDS = { "Node_Desc_req": [0x0002, [uint8_t, EmberNodeId], [EmberStatus]], - "Node_Desc_rsp": [0x8002, [EmberStatus,EmberNodeId, EmberNodeDescriptor], []], + "Node_Desc_rsp": [0x8002, [EmberStatus, EmberNodeId, EmberNodeDescriptor], []], + "Simple_Desc_req": [0x0004, [uint8_t, EmberNodeId, uint8_t], [EmberStatus]], + "Simple_Desc_rsp": [0x8004, [EmberStatus, EmberNodeId, EmberSimpleDescriptor], []], "Active_EP_req": [0x0005, [uint8_t, EmberNodeId], [EmberStatus]], "Active_EP_rsp": [0x8005, [EmberStatus, uint8_t, EmberNodeId, LVBytes], []], } diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index f5d08a311c..418ace9f92 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -32,7 +32,7 @@ import { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, + EmberNodeDescriptor, EmberSimpleDescriptor, } from './struct' export function deserialize(payload: any, schema: any[]) { @@ -82,5 +82,5 @@ export { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor + EmberNodeDescriptor, EmberSimpleDescriptor, } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 1f40bf39f4..a39a49efe2 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -608,3 +608,15 @@ export class EmberNodeDescriptor extends EzspStruct { ] } + +export class EmberSimpleDescriptor extends EzspStruct { + static _fields = [ + ['endpoint', basic.uint8_t], + ['profileid', basic.uint16_t], + ['deviceType', basic.uint16_t], + ['deviceVersion', basic.uint8_t], + ['inclusterlist', basic.WordList], + ['outclusterlist', basic.WordList], + ] +} + From 233f06b55669882ab11f09203fa05814ea834eb5 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 19 Feb 2021 10:58:57 +0300 Subject: [PATCH 33/63] del yargs --- src/adapter/ezsp/driver/commands.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index f1a69c7b08..e85ac76323 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -1,4 +1,3 @@ -import { array } from 'zigbee-herdsman/node_modules/@types/yargs'; import {/* Basic Types */ int8s, uint8_t, @@ -11,12 +10,12 @@ import {/* Basic Types */ /* Named Types */ EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberNodeId, EmberPanId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, - EmberGpKeyType, SecureEzspRandomNumber, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, + EmberGpKeyType, SecureEzspRandomNumber, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, - EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, - EzspNetworkScanType, EmberJoinDecision, EmberKeyType, - EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, - EzspZllNetworkOperation, + EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, + EzspNetworkScanType, EmberJoinDecision, EmberKeyType, + EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, + EzspZllNetworkOperation, /* Structs */ EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, From 3c7f421a10d97bf07925edb2ecc037539ab463b4 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 19 Feb 2021 13:51:19 +0300 Subject: [PATCH 34/63] leave device --- src/adapter/ezsp/adapter/ezspAdapter.ts | 779 ++++++++++++------------ 1 file changed, 389 insertions(+), 390 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 921d581296..96bfab2ce0 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -1,391 +1,390 @@ -/* istanbul ignore file */ -/* eslint-disable */ -import { - NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, - DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, - StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, -} from '../../tstype'; -import Debug from "debug"; -import Adapter from '../../adapter'; -const debug = Debug("zigbee-herdsman:adapter:ezsp"); -import {Ezsp, Driver} from '../driver'; -import { EmberApsFrame } from '../driver/types/struct'; -import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; -import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; -import * as Events from '../../events'; -import * as Zcl from '../../../zcl'; -import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; -import {Queue, Waitress, Wait} from '../../../utils'; - - -interface WaitressMatcher { - address: number | string; - endpoint: number; - transactionSequenceNumber?: number; - frameType: FrameType; - clusterID: number; - commandIdentifier: number; - direction: number; -} - -class EZSPAdapter extends Adapter { - private driver: Driver; - private port: SerialPortOptions; - private transactionID: number; - - public constructor(networkOptions: NetworkOptions, - serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { - super(networkOptions, serialPortOptions, backupPath, adapterOptions); - this.transactionID = 1; - this.port = serialPortOptions; - this.driver = new Driver(); - this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); - this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); - this.driver.on('incomingMessage', this.processMessage.bind(this)); - } - - private nextTransactionID(): number { - this.transactionID++; - - if (this.transactionID > 255) { - this.transactionID = 1; - } - - return this.transactionID; - } - - private async processMessage(frame: any) { - // todo - debug(`processMessage: ${JSON.stringify(frame)}`); - if (!frame.senderEui64) { - frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) - } - if (frame.apsFrame.profileId == 0) { - if ( - frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && - frame.apsFrame.destinationEndpoint == 0) { - let nwk, rst, ieee; - [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); - [ieee, rst] = EmberEUI64.deserialize(EmberEUI64, rst as Buffer); - ieee = new EmberEUI64(ieee); - debug("ZDO Device announce: %s, %s", nwk, ieee.toString()); - this.handleDeviceJoin([nwk, ieee]); - } - } else if (frame.apsFrame.profileId == 260) { - try { - const payload: Events.ZclDataPayload = { - frame: ZclFrame.fromBuffer(frame.apsFrame.clusterId, frame.message), - address: frame.sender, - endpoint: frame.apsFrame.sourceEndpoint, - linkquality: frame.lqi, - groupID: frame.apsFrame.groupId, - }; - - //this.waitress.resolve(payload); - this.emit(Events.Events.zclData, payload); - } catch (error) { - const payload: Events.RawDataPayload = { - clusterID: frame.apsFrame.clusterId, - data: frame.message, - address: frame.sender, - endpoint: frame.apsFrame.sourceEndpoint, - linkquality: frame.lqi, - groupID: frame.apsFrame.groupId, - }; - - this.emit(Events.Events.rawData, payload); - } - } - this.emit('event', frame); - } - - private async handleDeviceJoin(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device join request received: %s %s', nwk, ieee.toString('hex')); - const payload: Events.DeviceJoinedPayload = { - networkAddress: nwk, - ieeeAddr: `0x${ieee.toString('hex')}`, - }; - - if (nwk == 0) { - const nd = await this.nodeDescriptor(nwk); - } else { - this.emit(Events.Events.deviceJoined, payload); - } - } - - private handleDeviceLeft(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device left network request received: %s %s', nwk, ieee); - // let devices = this.getDevices(); - - // let idx = devices.findIndex(d => d.nodeId === nwk && d.eui64 === ieee.toString()); - // if (idx >= 0) { - // devices = devices.splice(idx, 1); - // writeFileSync(deviceDbPath, JSON.stringify(devices), 'utf8'); - // } - } - - /** - * Adapter methods - */ - public async start(): Promise { - await this.driver.startup(this.port.path, { - baudRate: this.port.baudRate || 115200, - parity: 'none', - stopBits: 1, - xon: true, - xoff: true - }, this.networkOptions); - return Promise.resolve("resumed"); - } - - public async stop(): Promise { - await this.driver.stop(); - } - - public static async isValidPath(path: string): Promise { - // todo - return true; - } - - public static async autoDetectPath(): Promise { - // todo - return ''; - } - - public async getCoordinator(): Promise { - // todo - return { - networkAddress: 0x0000, - manufacturerID: 0x1135, - ieeeAddr: '', - endpoints: [], - }; - } - - public async permitJoin(seconds: number, networkAddress: number): Promise { - // todo - await this.driver.permitJoining(seconds); - return Promise.resolve(); - } - - public async getCoordinatorVersion(): Promise { - // todo - return {type: '', meta: {}}; - } - - public async reset(type: 'soft' | 'hard'): Promise { - return Promise.reject(); - } - - public async supportsLED(): Promise { - return false; - } - - public async setLED(enabled: boolean): Promise { - return Promise.reject(); - } - - public async lqi(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async routingTable(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async nodeDescriptor(networkAddress: number): Promise { - // todo - try { - debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); - const result = await this.nodeDescriptorInternal(networkAddress); - return result; - } catch (error) { - debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); - throw error; - } - } - - private async nodeDescriptorInternal(networkAddress: number): Promise { - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Node_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const descriptor = await response.start().promise; - debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); - const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); - debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); - return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; - }); - } - - public async activeEndpoints(networkAddress: number): Promise { - // todo - debug(`Requesting 'Active endpoints' for '${networkAddress}'`); - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Active_EP_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); - await this.driver.request(networkAddress, frame, payload); - const activeEp = await response.start().promise; - debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); - const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); - debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); - return {endpoints: [...message[3]]}; - }, networkAddress); - } - - public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { - // todo - debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Simple_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = endpointID; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const message = await response.start().promise; - debug(`activeEndpoints got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); - const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); - debug(`activeEndpoints got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); - return { - profileID: descriptor.payload.profileid, - endpointID: descriptor.payload.endpoint, - deviceID: descriptor.sender, - inputClusters: descriptor.payload.inclusterlist, - outputClusters: descriptor.payload.outclusterlist, - }; - }, networkAddress); - } - - public waitFor( - networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, - transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {promise: Promise; cancel: () => void} { - // todo - return {cancel: undefined, promise: undefined}; - } - - public async sendZclFrameToEndpoint( - ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, - disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, - ): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { - // todo - return Promise.resolve(); - } - - public async bind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint?: number - ): Promise { - // todo - return Promise.reject(); - } - - public async unbind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint: number - ): Promise { - // todo - return Promise.reject(); - } - - public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { - // todo - return Promise.reject(); - } - - public async getNetworkParameters(): Promise { - return { - panID: this.driver.networkParams.panId, - extendedPanID: this.driver.networkParams.extendedPanId[0], - channel: this.driver.networkParams.radioChannel - }; - } - - public async supportsBackup(): Promise { - //todo - return false; - } - - public async backup(): Promise { - // todo - return Promise.reject(); - } - - public async restoreChannelInterPAN(): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcast( - zclFrame: ZclFrame, timeout: number - ): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcastWithResponse( - zclFrame: ZclFrame, timeout: number - ): Promise { - throw new Error("not supported"); - } - - public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { - throw new Error("not supported"); - } - - public async setTransmitPower(value: number): Promise { - // todo - } - - public async setChannelInterPAN(channel: number): Promise { - //todo - } -} - - +/* istanbul ignore file */ +/* eslint-disable */ +import { + NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, + DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, + StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, +} from '../../tstype'; +import Debug from "debug"; +import Adapter from '../../adapter'; +const debug = Debug("zigbee-herdsman:adapter:ezsp"); +import {Ezsp, Driver} from '../driver'; +import { EmberApsFrame } from '../driver/types/struct'; +import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; +import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; +import * as Events from '../../events'; +import * as Zcl from '../../../zcl'; +import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; +import {Queue, Waitress, Wait} from '../../../utils'; + + +interface WaitressMatcher { + address: number | string; + endpoint: number; + transactionSequenceNumber?: number; + frameType: FrameType; + clusterID: number; + commandIdentifier: number; + direction: number; +} + +class EZSPAdapter extends Adapter { + private driver: Driver; + private port: SerialPortOptions; + private transactionID: number; + + public constructor(networkOptions: NetworkOptions, + serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { + super(networkOptions, serialPortOptions, backupPath, adapterOptions); + this.transactionID = 1; + this.port = serialPortOptions; + this.driver = new Driver(); + this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); + this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); + this.driver.on('incomingMessage', this.processMessage.bind(this)); + } + + private nextTransactionID(): number { + this.transactionID++; + + if (this.transactionID > 255) { + this.transactionID = 1; + } + + return this.transactionID; + } + + private async processMessage(frame: any) { + // todo + debug(`processMessage: ${JSON.stringify(frame)}`); + if (!frame.senderEui64) { + frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) + } + if (frame.apsFrame.profileId == 0) { + if ( + frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && + frame.apsFrame.destinationEndpoint == 0) { + let nwk, rst, ieee; + [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + [ieee, rst] = EmberEUI64.deserialize(EmberEUI64, rst as Buffer); + ieee = new EmberEUI64(ieee); + debug("ZDO Device announce: %s, %s", nwk, ieee.toString()); + this.handleDeviceJoin([nwk, ieee]); + } + } else if (frame.apsFrame.profileId == 260) { + try { + const payload: Events.ZclDataPayload = { + frame: ZclFrame.fromBuffer(frame.apsFrame.clusterId, frame.message), + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + //this.waitress.resolve(payload); + this.emit(Events.Events.zclData, payload); + } catch (error) { + const payload: Events.RawDataPayload = { + clusterID: frame.apsFrame.clusterId, + data: frame.message, + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + this.emit(Events.Events.rawData, payload); + } + } + this.emit('event', frame); + } + + private async handleDeviceJoin(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device join request received: %s %s', nwk, ieee.toString('hex')); + const payload: Events.DeviceJoinedPayload = { + networkAddress: nwk, + ieeeAddr: `0x${ieee.toString('hex')}`, + }; + + if (nwk == 0) { + const nd = await this.nodeDescriptor(nwk); + } else { + this.emit(Events.Events.deviceJoined, payload); + } + } + + private handleDeviceLeft(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device left network request received: %s %s', nwk, ieee); + + const payload: Events.DeviceLeavePayload = { + networkAddress: nwk, + ieeeAddr: `0x${ieee.toString('hex')}`, + }; + this.emit(Events.Events.deviceLeave, payload) + } + + /** + * Adapter methods + */ + public async start(): Promise { + await this.driver.startup(this.port.path, { + baudRate: this.port.baudRate || 115200, + parity: 'none', + stopBits: 1, + xon: true, + xoff: true + }, this.networkOptions); + return Promise.resolve("resumed"); + } + + public async stop(): Promise { + await this.driver.stop(); + } + + public static async isValidPath(path: string): Promise { + // todo + return true; + } + + public static async autoDetectPath(): Promise { + // todo + return ''; + } + + public async getCoordinator(): Promise { + // todo + return { + networkAddress: 0x0000, + manufacturerID: 0x1135, + ieeeAddr: '', + endpoints: [], + }; + } + + public async permitJoin(seconds: number, networkAddress: number): Promise { + // todo + await this.driver.permitJoining(seconds); + return Promise.resolve(); + } + + public async getCoordinatorVersion(): Promise { + // todo + return {type: '', meta: {}}; + } + + public async reset(type: 'soft' | 'hard'): Promise { + return Promise.reject(); + } + + public async supportsLED(): Promise { + return false; + } + + public async setLED(enabled: boolean): Promise { + return Promise.reject(); + } + + public async lqi(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async routingTable(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async nodeDescriptor(networkAddress: number): Promise { + // todo + try { + debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); + const result = await this.nodeDescriptorInternal(networkAddress); + return result; + } catch (error) { + debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); + throw error; + } + } + + private async nodeDescriptorInternal(networkAddress: number): Promise { + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Node_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const descriptor = await response.start().promise; + debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); + const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); + debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); + return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + }); + } + + public async activeEndpoints(networkAddress: number): Promise { + // todo + debug(`Requesting 'Active endpoints' for '${networkAddress}'`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Active_EP_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); + await this.driver.request(networkAddress, frame, payload); + const activeEp = await response.start().promise; + debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); + const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); + debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); + return {endpoints: [...message[3]]}; + }, networkAddress); + } + + public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { + // todo + debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Simple_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = endpointID; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug(`activeEndpoints got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); + const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); + debug(`activeEndpoints got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); + return { + profileID: descriptor.payload.profileid, + endpointID: descriptor.payload.endpoint, + deviceID: descriptor.sender, + inputClusters: descriptor.payload.inclusterlist, + outputClusters: descriptor.payload.outclusterlist, + }; + }, networkAddress); + } + + public waitFor( + networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, + transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, + ): {promise: Promise; cancel: () => void} { + // todo + return {cancel: undefined, promise: undefined}; + } + + public async sendZclFrameToEndpoint( + ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, + disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, + ): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { + // todo + return Promise.resolve(); + } + + public async bind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint?: number + ): Promise { + // todo + return Promise.reject(); + } + + public async unbind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint: number + ): Promise { + // todo + return Promise.reject(); + } + + public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { + // todo + return Promise.reject(); + } + + public async getNetworkParameters(): Promise { + return { + panID: this.driver.networkParams.panId, + extendedPanID: this.driver.networkParams.extendedPanId[0], + channel: this.driver.networkParams.radioChannel + }; + } + + public async supportsBackup(): Promise { + //todo + return false; + } + + public async backup(): Promise { + // todo + return Promise.reject(); + } + + public async restoreChannelInterPAN(): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcast( + zclFrame: ZclFrame, timeout: number + ): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcastWithResponse( + zclFrame: ZclFrame, timeout: number + ): Promise { + throw new Error("not supported"); + } + + public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { + throw new Error("not supported"); + } + + public async setTransmitPower(value: number): Promise { + // todo + } + + public async setChannelInterPAN(channel: number): Promise { + //todo + } +} + + export default EZSPAdapter; \ No newline at end of file From a495528735d2f1186c71a697ec71b3b2f24058ff Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 19 Feb 2021 13:51:59 +0300 Subject: [PATCH 35/63] simple decriptor start --- src/adapter/ezsp/adapter/ezspAdapter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 96bfab2ce0..198f0e0b54 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -263,10 +263,11 @@ class EZSPAdapter extends Adapter { frame.profileId = 0; frame.sequence = this.nextTransactionID(); frame.sourceEndpoint = 0; - frame.destinationEndpoint = endpointID; + frame.destinationEndpoint = 0; frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress); + debug('frame %o',frame); + const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpointID); const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); await this.driver.request(networkAddress, frame, payload); const message = await response.start().promise; From 7d60d44468572ca80f1689b23de95d1e3be89046 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Fri, 19 Feb 2021 14:13:09 +0300 Subject: [PATCH 36/63] simple descriptor --- src/adapter/ezsp/adapter/ezspAdapter.ts | 779 ++++++++++++------------ src/adapter/ezsp/driver/commands.ts | 2 +- src/adapter/ezsp/driver/types/basic.ts | 14 +- src/adapter/ezsp/driver/types/struct.ts | 8 +- 4 files changed, 401 insertions(+), 402 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 198f0e0b54..4c098d66eb 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -1,391 +1,390 @@ -/* istanbul ignore file */ -/* eslint-disable */ -import { - NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, - DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, - StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, -} from '../../tstype'; -import Debug from "debug"; -import Adapter from '../../adapter'; -const debug = Debug("zigbee-herdsman:adapter:ezsp"); -import {Ezsp, Driver} from '../driver'; -import { EmberApsFrame } from '../driver/types/struct'; -import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; -import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; -import * as Events from '../../events'; -import * as Zcl from '../../../zcl'; -import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; -import {Queue, Waitress, Wait} from '../../../utils'; - - -interface WaitressMatcher { - address: number | string; - endpoint: number; - transactionSequenceNumber?: number; - frameType: FrameType; - clusterID: number; - commandIdentifier: number; - direction: number; -} - -class EZSPAdapter extends Adapter { - private driver: Driver; - private port: SerialPortOptions; - private transactionID: number; - - public constructor(networkOptions: NetworkOptions, - serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { - super(networkOptions, serialPortOptions, backupPath, adapterOptions); - this.transactionID = 1; - this.port = serialPortOptions; - this.driver = new Driver(); - this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); - this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); - this.driver.on('incomingMessage', this.processMessage.bind(this)); - } - - private nextTransactionID(): number { - this.transactionID++; - - if (this.transactionID > 255) { - this.transactionID = 1; - } - - return this.transactionID; - } - - private async processMessage(frame: any) { - // todo - debug(`processMessage: ${JSON.stringify(frame)}`); - if (!frame.senderEui64) { - frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) - } - if (frame.apsFrame.profileId == 0) { - if ( - frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && - frame.apsFrame.destinationEndpoint == 0) { - let nwk, rst, ieee; - [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); - [ieee, rst] = EmberEUI64.deserialize(EmberEUI64, rst as Buffer); - ieee = new EmberEUI64(ieee); - debug("ZDO Device announce: %s, %s", nwk, ieee.toString()); - this.handleDeviceJoin([nwk, ieee]); - } - } else if (frame.apsFrame.profileId == 260) { - try { - const payload: Events.ZclDataPayload = { - frame: ZclFrame.fromBuffer(frame.apsFrame.clusterId, frame.message), - address: frame.sender, - endpoint: frame.apsFrame.sourceEndpoint, - linkquality: frame.lqi, - groupID: frame.apsFrame.groupId, - }; - - //this.waitress.resolve(payload); - this.emit(Events.Events.zclData, payload); - } catch (error) { - const payload: Events.RawDataPayload = { - clusterID: frame.apsFrame.clusterId, - data: frame.message, - address: frame.sender, - endpoint: frame.apsFrame.sourceEndpoint, - linkquality: frame.lqi, - groupID: frame.apsFrame.groupId, - }; - - this.emit(Events.Events.rawData, payload); - } - } - this.emit('event', frame); - } - - private async handleDeviceJoin(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device join request received: %s %s', nwk, ieee.toString('hex')); - const payload: Events.DeviceJoinedPayload = { - networkAddress: nwk, - ieeeAddr: `0x${ieee.toString('hex')}`, - }; - - if (nwk == 0) { - const nd = await this.nodeDescriptor(nwk); - } else { - this.emit(Events.Events.deviceJoined, payload); - } - } - - private handleDeviceLeft(arr: any[]) { - // todo - let [nwk, ieee] = arr; - debug('Device left network request received: %s %s', nwk, ieee); - - const payload: Events.DeviceLeavePayload = { - networkAddress: nwk, - ieeeAddr: `0x${ieee.toString('hex')}`, - }; - this.emit(Events.Events.deviceLeave, payload) - } - - /** - * Adapter methods - */ - public async start(): Promise { - await this.driver.startup(this.port.path, { - baudRate: this.port.baudRate || 115200, - parity: 'none', - stopBits: 1, - xon: true, - xoff: true - }, this.networkOptions); - return Promise.resolve("resumed"); - } - - public async stop(): Promise { - await this.driver.stop(); - } - - public static async isValidPath(path: string): Promise { - // todo - return true; - } - - public static async autoDetectPath(): Promise { - // todo - return ''; - } - - public async getCoordinator(): Promise { - // todo - return { - networkAddress: 0x0000, - manufacturerID: 0x1135, - ieeeAddr: '', - endpoints: [], - }; - } - - public async permitJoin(seconds: number, networkAddress: number): Promise { - // todo - await this.driver.permitJoining(seconds); - return Promise.resolve(); - } - - public async getCoordinatorVersion(): Promise { - // todo - return {type: '', meta: {}}; - } - - public async reset(type: 'soft' | 'hard'): Promise { - return Promise.reject(); - } - - public async supportsLED(): Promise { - return false; - } - - public async setLED(enabled: boolean): Promise { - return Promise.reject(); - } - - public async lqi(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async routingTable(networkAddress: number): Promise { - // todo - return Promise.reject(); - } - - public async nodeDescriptor(networkAddress: number): Promise { - // todo - try { - debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); - const result = await this.nodeDescriptorInternal(networkAddress); - return result; - } catch (error) { - debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); - throw error; - } - } - - private async nodeDescriptorInternal(networkAddress: number): Promise { - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Node_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const descriptor = await response.start().promise; - debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); - const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); - debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); - return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; - }); - } - - public async activeEndpoints(networkAddress: number): Promise { - // todo - debug(`Requesting 'Active endpoints' for '${networkAddress}'`); - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Active_EP_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); - await this.driver.request(networkAddress, frame, payload); - const activeEp = await response.start().promise; - debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); - const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); - debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); - return {endpoints: [...message[3]]}; - }, networkAddress); - } - - public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { - // todo - debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); - return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Simple_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - debug('frame %o',frame); - const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpointID); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const message = await response.start().promise; - debug(`activeEndpoints got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); - const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); - debug(`activeEndpoints got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); - return { - profileID: descriptor.payload.profileid, - endpointID: descriptor.payload.endpoint, - deviceID: descriptor.sender, - inputClusters: descriptor.payload.inclusterlist, - outputClusters: descriptor.payload.outclusterlist, - }; - }, networkAddress); - } - - public waitFor( - networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, - transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {promise: Promise; cancel: () => void} { - // todo - return {cancel: undefined, promise: undefined}; - } - - public async sendZclFrameToEndpoint( - ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, - disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, - ): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { - // todo - return Promise.reject(); - } - - public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { - // todo - return Promise.resolve(); - } - - public async bind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint?: number - ): Promise { - // todo - return Promise.reject(); - } - - public async unbind( - destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, - clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', - destinationEndpoint: number - ): Promise { - // todo - return Promise.reject(); - } - - public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { - // todo - return Promise.reject(); - } - - public async getNetworkParameters(): Promise { - return { - panID: this.driver.networkParams.panId, - extendedPanID: this.driver.networkParams.extendedPanId[0], - channel: this.driver.networkParams.radioChannel - }; - } - - public async supportsBackup(): Promise { - //todo - return false; - } - - public async backup(): Promise { - // todo - return Promise.reject(); - } - - public async restoreChannelInterPAN(): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcast( - zclFrame: ZclFrame, timeout: number - ): Promise { - // todo - throw new Error("not supported"); - } - - public async sendZclFrameInterPANBroadcastWithResponse( - zclFrame: ZclFrame, timeout: number - ): Promise { - throw new Error("not supported"); - } - - public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { - throw new Error("not supported"); - } - - public async setTransmitPower(value: number): Promise { - // todo - } - - public async setChannelInterPAN(channel: number): Promise { - //todo - } -} - - +/* istanbul ignore file */ +/* eslint-disable */ +import { + NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, + DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, + StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, +} from '../../tstype'; +import Debug from "debug"; +import Adapter from '../../adapter'; +const debug = Debug("zigbee-herdsman:adapter:ezsp"); +import {Ezsp, Driver} from '../driver'; +import { EmberApsFrame } from '../driver/types/struct'; +import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; +import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; +import * as Events from '../../events'; +import * as Zcl from '../../../zcl'; +import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; +import {Queue, Waitress, Wait} from '../../../utils'; + + +interface WaitressMatcher { + address: number | string; + endpoint: number; + transactionSequenceNumber?: number; + frameType: FrameType; + clusterID: number; + commandIdentifier: number; + direction: number; +} + +class EZSPAdapter extends Adapter { + private driver: Driver; + private port: SerialPortOptions; + private transactionID: number; + + public constructor(networkOptions: NetworkOptions, + serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { + super(networkOptions, serialPortOptions, backupPath, adapterOptions); + this.transactionID = 1; + this.port = serialPortOptions; + this.driver = new Driver(); + this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); + this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); + this.driver.on('incomingMessage', this.processMessage.bind(this)); + } + + private nextTransactionID(): number { + this.transactionID++; + + if (this.transactionID > 255) { + this.transactionID = 1; + } + + return this.transactionID; + } + + private async processMessage(frame: any) { + // todo + debug(`processMessage: ${JSON.stringify(frame)}`); + if (!frame.senderEui64) { + frame.senderEui64 = await this.driver.networkIdToEUI64(frame.sender) + } + if (frame.apsFrame.profileId == 0) { + if ( + frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && + frame.apsFrame.destinationEndpoint == 0) { + let nwk, rst, ieee; + [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); + [ieee, rst] = EmberEUI64.deserialize(EmberEUI64, rst as Buffer); + ieee = new EmberEUI64(ieee); + debug("ZDO Device announce: %s, %s", nwk, ieee.toString()); + this.handleDeviceJoin([nwk, ieee]); + } + } else if (frame.apsFrame.profileId == 260) { + try { + const payload: Events.ZclDataPayload = { + frame: ZclFrame.fromBuffer(frame.apsFrame.clusterId, frame.message), + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + //this.waitress.resolve(payload); + this.emit(Events.Events.zclData, payload); + } catch (error) { + const payload: Events.RawDataPayload = { + clusterID: frame.apsFrame.clusterId, + data: frame.message, + address: frame.sender, + endpoint: frame.apsFrame.sourceEndpoint, + linkquality: frame.lqi, + groupID: frame.apsFrame.groupId, + }; + + this.emit(Events.Events.rawData, payload); + } + } + this.emit('event', frame); + } + + private async handleDeviceJoin(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device join request received: %s %s', nwk, ieee.toString('hex')); + const payload: Events.DeviceJoinedPayload = { + networkAddress: nwk, + ieeeAddr: `0x${ieee.toString('hex')}`, + }; + + if (nwk == 0) { + const nd = await this.nodeDescriptor(nwk); + } else { + this.emit(Events.Events.deviceJoined, payload); + } + } + + private handleDeviceLeft(arr: any[]) { + // todo + let [nwk, ieee] = arr; + debug('Device left network request received: %s %s', nwk, ieee); + + const payload: Events.DeviceLeavePayload = { + networkAddress: nwk, + ieeeAddr: `0x${ieee.toString('hex')}`, + }; + this.emit(Events.Events.deviceLeave, payload); + } + + /** + * Adapter methods + */ + public async start(): Promise { + await this.driver.startup(this.port.path, { + baudRate: this.port.baudRate || 115200, + parity: 'none', + stopBits: 1, + xon: true, + xoff: true + }, this.networkOptions); + return Promise.resolve("resumed"); + } + + public async stop(): Promise { + await this.driver.stop(); + } + + public static async isValidPath(path: string): Promise { + // todo + return true; + } + + public static async autoDetectPath(): Promise { + // todo + return ''; + } + + public async getCoordinator(): Promise { + // todo + return { + networkAddress: 0x0000, + manufacturerID: 0x1135, + ieeeAddr: '', + endpoints: [], + }; + } + + public async permitJoin(seconds: number, networkAddress: number): Promise { + // todo + await this.driver.permitJoining(seconds); + return Promise.resolve(); + } + + public async getCoordinatorVersion(): Promise { + // todo + return {type: '', meta: {}}; + } + + public async reset(type: 'soft' | 'hard'): Promise { + return Promise.reject(); + } + + public async supportsLED(): Promise { + return false; + } + + public async setLED(enabled: boolean): Promise { + return Promise.reject(); + } + + public async lqi(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async routingTable(networkAddress: number): Promise { + // todo + return Promise.reject(); + } + + public async nodeDescriptor(networkAddress: number): Promise { + // todo + try { + debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); + const result = await this.nodeDescriptorInternal(networkAddress); + return result; + } catch (error) { + debug(`Node descriptor request for '${networkAddress}' failed (${error}), retry`); + throw error; + } + } + + private async nodeDescriptorInternal(networkAddress: number): Promise { + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Node_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const descriptor = await response.start().promise; + debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); + const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); + debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); + return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + }); + } + + public async activeEndpoints(networkAddress: number): Promise { + // todo + debug(`Requesting 'Active endpoints' for '${networkAddress}'`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Active_EP_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); + await this.driver.request(networkAddress, frame, payload); + const activeEp = await response.start().promise; + debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); + const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); + debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); + return {endpoints: [...message[3]]}; + }, networkAddress); + } + + public async simpleDescriptor(networkAddress: number, endpointID: number): Promise { + // todo + debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Simple_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_NONE; + const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpointID); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug(`simpleDescriptor got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); + const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); + debug(`simpleDescriptor got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); + return { + profileID: descriptor[2].profileid, + endpointID: descriptor[2].endpoint, + deviceID: descriptor[2].deviceid, + inputClusters: descriptor[2].inclusterlist, + outputClusters: descriptor[2].outclusterlist, + }; + }, networkAddress); + } + + public waitFor( + networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, + transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, + ): {promise: Promise; cancel: () => void} { + // todo + return {cancel: undefined, promise: undefined}; + } + + public async sendZclFrameToEndpoint( + ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, + disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, + ): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { + // todo + return Promise.reject(); + } + + public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise { + // todo + return Promise.resolve(); + } + + public async bind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint?: number + ): Promise { + // todo + return Promise.reject(); + } + + public async unbind( + destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number, + clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', + destinationEndpoint: number + ): Promise { + // todo + return Promise.reject(); + } + + public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { + // todo + return Promise.reject(); + } + + public async getNetworkParameters(): Promise { + return { + panID: this.driver.networkParams.panId, + extendedPanID: this.driver.networkParams.extendedPanId[0], + channel: this.driver.networkParams.radioChannel + }; + } + + public async supportsBackup(): Promise { + //todo + return false; + } + + public async backup(): Promise { + // todo + return Promise.reject(); + } + + public async restoreChannelInterPAN(): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANToIeeeAddr(zclFrame: ZclFrame, ieeeAddr: string): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcast( + zclFrame: ZclFrame, timeout: number + ): Promise { + // todo + throw new Error("not supported"); + } + + public async sendZclFrameInterPANBroadcastWithResponse( + zclFrame: ZclFrame, timeout: number + ): Promise { + throw new Error("not supported"); + } + + public async sendZclFrameInterPANIeeeAddr(zclFrame: ZclFrame, ieeeAddr: any): Promise { + throw new Error("not supported"); + } + + public async setTransmitPower(value: number): Promise { + // todo + } + + public async setChannelInterPAN(channel: number): Promise { + //todo + } +} + + export default EZSPAdapter; \ No newline at end of file diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index e85ac76323..feeb040288 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -698,7 +698,7 @@ export const ZDO_COMMANDS = { "Node_Desc_req": [0x0002, [uint8_t, EmberNodeId], [EmberStatus]], "Node_Desc_rsp": [0x8002, [EmberStatus, EmberNodeId, EmberNodeDescriptor], []], "Simple_Desc_req": [0x0004, [uint8_t, EmberNodeId, uint8_t], [EmberStatus]], - "Simple_Desc_rsp": [0x8004, [EmberStatus, EmberNodeId, EmberSimpleDescriptor], []], + "Simple_Desc_rsp": [0x8004, [uint8_t, EmberStatus, EmberNodeId, uint8_t, EmberSimpleDescriptor], []], "Active_EP_req": [0x0005, [uint8_t, EmberNodeId], [EmberStatus]], "Active_EP_rsp": [0x8005, [EmberStatus, uint8_t, EmberNodeId, LVBytes], []], } diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index 5dd8ca3a45..f5e0ae7a06 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -114,7 +114,7 @@ export abstract class List { var item; var r: any[] = []; while (data) { - [item, data] = (r)._itemtype.deserialize((r)._itemtype, data); + [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); } return [r, data]; @@ -133,7 +133,7 @@ class _LVList extends List { var r: any[] = []; [length, data] = [data[0], data.slice(1)]; for (var i = 0; i < length; i++) { - [item, data] = (r)._itemtype.deserialize((r)._itemtype, data); + [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); } return [r, data]; @@ -141,15 +141,15 @@ class _LVList extends List { } export function list(itemtype: any) : List { class ConreteList extends List { + static itemtype = itemtype } - (LVList.prototype)['_itemtype'] = itemtype; return ConreteList; } export function LVList(itemtype: any) : List { class LVList extends _LVList { + static itemtype = itemtype } - (LVList.prototype)['itemtype'] = itemtype; return LVList; } @@ -162,14 +162,14 @@ export class WordList extends List { class _FixedList extends List { static serialize(cls: any, value: any[]) { - const data = value.map(i => cls._itemtype.serialize(cls._itemtype, i)[0]); + const data = value.map(i => cls.itemtype.serialize(cls.itemtype, i)[0]); return Buffer.from(data); } static deserialize(cls: any, data: Buffer) { let item; let r: any[] = []; for (var i = 0; i < cls._length; i++) { - [item, data] = cls._itemtype.deserialize(cls._itemtype, data); + [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); } return [r, data]; @@ -181,7 +181,7 @@ export function fixed_list(length: number, itemtype: any) : { deserialize(cls : any, data : Buffer) : any; } { class FixedList extends _FixedList { - static _itemtype = itemtype; + static itemtype = itemtype; static _length = length; } return FixedList; diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index a39a49efe2..07d5a12f7d 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -613,10 +613,10 @@ export class EmberSimpleDescriptor extends EzspStruct { static _fields = [ ['endpoint', basic.uint8_t], ['profileid', basic.uint16_t], - ['deviceType', basic.uint16_t], - ['deviceVersion', basic.uint8_t], - ['inclusterlist', basic.WordList], - ['outclusterlist', basic.WordList], + ['deviceid', basic.uint16_t], + ['deviceversion', basic.uint8_t], + ['inclusterlist', basic.LVList(basic.uint16_t)], + ['outclusterlist', basic.LVList(basic.uint16_t)], ] } From ad33db062aa49d6c7c222c16e8fdcf4d75f4cd20 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 19 Feb 2021 14:24:33 +0300 Subject: [PATCH 37/63] descriptor & debug --- src/adapter/ezsp/adapter/ezspAdapter.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 4c098d66eb..0bc02bc800 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -32,7 +32,7 @@ class EZSPAdapter extends Adapter { private driver: Driver; private port: SerialPortOptions; private transactionID: number; - + public constructor(networkOptions: NetworkOptions, serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { super(networkOptions, serialPortOptions, backupPath, adapterOptions); @@ -62,7 +62,7 @@ class EZSPAdapter extends Adapter { } if (frame.apsFrame.profileId == 0) { if ( - frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && + frame.apsFrame.clusterId == EmberZDOCmd.Device_annce && frame.apsFrame.destinationEndpoint == 0) { let nwk, rst, ieee; [nwk, rst] = uint16_t.deserialize(uint16_t, frame.message.slice(1)); @@ -270,15 +270,15 @@ class EZSPAdapter extends Adapter { const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); await this.driver.request(networkAddress, frame, payload); const message = await response.start().promise; - debug(`simpleDescriptor got Simple Descriptor payload: ${JSON.stringify(message.payload)}`); + debug('simpleDescriptor got Simple Descriptor payload %O:', message.payload); const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); - debug(`simpleDescriptor got Simple Descriptor parsed: ${JSON.stringify(descriptor)}`); + debug('simpleDescriptor got Simple Descriptor parsed: %O',descriptor); return { - profileID: descriptor[2].profileid, - endpointID: descriptor[2].endpoint, - deviceID: descriptor[2].deviceid, - inputClusters: descriptor[2].inclusterlist, - outputClusters: descriptor[2].outclusterlist, + profileID: descriptor[4].profileid, + endpointID: descriptor[4].endpoint, + deviceID: descriptor[4].deviceid, + inputClusters: descriptor[4].inclusterlist, + outputClusters: descriptor[4].outclusterlist, }; }, networkAddress); } @@ -387,4 +387,4 @@ class EZSPAdapter extends Adapter { } -export default EZSPAdapter; \ No newline at end of file +export default EZSPAdapter; From 997ce957ca995974a09cea45174a8cfb4a0220d6 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 19 Feb 2021 14:42:16 +0300 Subject: [PATCH 38/63] sendZclFrameToEndpoint start --- src/adapter/ezsp/adapter/ezspAdapter.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 0bc02bc800..f0ec51be29 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -295,8 +295,19 @@ class EZSPAdapter extends Adapter { ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, ): Promise { - // todo - return Promise.reject(); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = zclFrame.Cluster.ID; + frame.profileId = 0x0104; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = sourceEndpoint || 0x01; + frame.destinationEndpoint = endpoint; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + // const response = this.driver.waitFor(networkAddress, ); + await this.driver.request(networkAddress, frame, zclFrame.toBuffer()); + return Promise.reject(); + }, networkAddress); } public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { From 4660cc10f5f3ee42320f72fededa25c54216716d Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sat, 20 Feb 2021 20:04:17 +0300 Subject: [PATCH 39/63] waitfor --- src/adapter/ezsp/adapter/ezspAdapter.ts | 126 +++++++++++++++++++----- 1 file changed, 104 insertions(+), 22 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index f0ec51be29..596fa8a4e7 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -22,22 +22,24 @@ interface WaitressMatcher { address: number | string; endpoint: number; transactionSequenceNumber?: number; - frameType: FrameType; clusterID: number; commandIdentifier: number; - direction: number; } class EZSPAdapter extends Adapter { private driver: Driver; private port: SerialPortOptions; private transactionID: number; + private waitress: Waitress; public constructor(networkOptions: NetworkOptions, serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { super(networkOptions, serialPortOptions, backupPath, adapterOptions); this.transactionID = 1; this.port = serialPortOptions; + this.waitress = new Waitress( + this.waitressValidator, this.waitressTimeoutFormatter + ); this.driver = new Driver(); this.driver.on('deviceJoined', this.handleDeviceJoin.bind(this)); this.driver.on('deviceLeft', this.handleDeviceLeft.bind(this)); @@ -81,7 +83,7 @@ class EZSPAdapter extends Adapter { groupID: frame.apsFrame.groupId, }; - //this.waitress.resolve(payload); + this.waitress.resolve(payload); this.emit(Events.Events.zclData, payload); } catch (error) { const payload: Events.RawDataPayload = { @@ -283,33 +285,73 @@ class EZSPAdapter extends Adapter { }, networkAddress); } - public waitFor( - networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, - transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {promise: Promise; cancel: () => void} { - // todo - return {cancel: undefined, promise: undefined}; - } - public async sendZclFrameToEndpoint( ieeeAddr: string, networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, disableResponse: boolean, disableRecovery: boolean, sourceEndpoint?: number, ): Promise { return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = zclFrame.Cluster.ID; - frame.profileId = 0x0104; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = sourceEndpoint || 0x01; - frame.destinationEndpoint = endpoint; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - // const response = this.driver.waitFor(networkAddress, ); - await this.driver.request(networkAddress, frame, zclFrame.toBuffer()); - return Promise.reject(); + return this.sendZclFrameToEndpointInternal( + ieeeAddr, networkAddress, endpoint, sourceEndpoint || 1, zclFrame, timeout, disableResponse, + disableRecovery, 0, 0, false, false, false, null + ); }, networkAddress); } + private async sendZclFrameToEndpointInternal( + ieeeAddr: string, networkAddress: number, endpoint: number, sourceEndpoint: number, zclFrame: ZclFrame, + timeout: number, disableResponse: boolean, disableRecovery: boolean, responseAttempt: number, + dataRequestAttempt: number, checkedNetworkAddress: boolean, discoveredRoute: boolean, assocRemove: boolean, + assocRestore: {ieeeadr: string, nwkaddr: number, noderelation: number} + ): Promise { + debug('sendZclFrameToEndpointInternal %s:%i/%i (%i,%i,%i)', + ieeeAddr, networkAddress, endpoint, responseAttempt, dataRequestAttempt, this.driver.queue.count()); + let response = null; + const command = zclFrame.getCommand(); + if (command.hasOwnProperty('response') && disableResponse === false) { + response = this.waitForInternal( + networkAddress, endpoint, + zclFrame.Header.transactionSequenceNumber, zclFrame.Cluster.ID, command.response, timeout + ); + } else if (!zclFrame.Header.frameControl.disableDefaultResponse) { + response = this.waitForInternal( + networkAddress, endpoint, + zclFrame.Header.transactionSequenceNumber, zclFrame.Cluster.ID, Foundation.defaultRsp.ID, + timeout, + ); + } + + const frame = new EmberApsFrame(); + frame.clusterId = zclFrame.Cluster.ID; + frame.profileId = 0x0104; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = sourceEndpoint || 0x01; + frame.destinationEndpoint = endpoint; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + // const response = this.driver.waitFor(networkAddress, ); + const dataConfirmResult = await this.driver.request(networkAddress, frame, zclFrame.toBuffer()); + + if (response !== null) { + try { + const result = await response.start().promise; + return result; + } catch (error) { + debug('Response timeout (%s:%d,%d)', ieeeAddr, networkAddress, responseAttempt); + if (responseAttempt < 1 && !disableRecovery) { + return this.sendZclFrameToEndpointInternal( + ieeeAddr, networkAddress, endpoint, sourceEndpoint, zclFrame, timeout, disableResponse, + disableRecovery, responseAttempt + 1, dataRequestAttempt, checkedNetworkAddress, + discoveredRoute, assocRemove, assocRestore, + ); + } else { + throw error; + } + } + } else { + return null; + } + } + public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise { // todo return Promise.reject(); @@ -395,6 +437,46 @@ class EZSPAdapter extends Adapter { public async setChannelInterPAN(channel: number): Promise { //todo } + + private waitForInternal( + networkAddress: number, endpoint: number, transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, + ): {start: () => {promise: Promise}; cancel: () => void} { + const payload = { + address: networkAddress, endpoint, clusterID, commandIdentifier, + transactionSequenceNumber, + }; + + const waiter = this.waitress.waitFor(payload, timeout); + const cancel = (): void => this.waitress.remove(waiter.ID); + return {start: waiter.start, cancel}; + } + + 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.waitForInternal( + networkAddress, endpoint, transactionSequenceNumber, clusterID, + commandIdentifier, timeout, + ); + + return {cancel: waiter.cancel, promise: waiter.start().promise}; + } + + private waitressTimeoutFormatter(matcher: WaitressMatcher, timeout: number): string { + return `Timeout - ${matcher.address} - ${matcher.endpoint}` + + ` - ${matcher.transactionSequenceNumber} - ${matcher.clusterID}` + + ` - ${matcher.commandIdentifier} after ${timeout}ms`; + } + + private waitressValidator(payload: Events.ZclDataPayload, matcher: WaitressMatcher): boolean { + const transactionSequenceNumber = payload.frame.Header.transactionSequenceNumber; + return (!matcher.address || payload.address === matcher.address) && + payload.endpoint === matcher.endpoint && + (!matcher.transactionSequenceNumber || transactionSequenceNumber === matcher.transactionSequenceNumber) && + payload.frame.Cluster.ID === matcher.clusterID && + matcher.commandIdentifier === payload.frame.Header.commandIdentifier; + } } From 56acea129f9eb5c238b6aabe3b324ac41f848c4f Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sat, 20 Feb 2021 20:59:52 +0300 Subject: [PATCH 40/63] getCoordinator --- src/adapter/ezsp/adapter/ezspAdapter.ts | 60 +++++++++++++++++++++---- src/adapter/ezsp/driver/driver.ts | 8 ++-- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 596fa8a4e7..3879880474 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -158,13 +158,58 @@ class EZSPAdapter extends Adapter { } public async getCoordinator(): Promise { - // todo - return { - networkAddress: 0x0000, - manufacturerID: 0x1135, - ieeeAddr: '', - endpoints: [], - }; + return this.driver.queue.execute(async () => { + const networkAddress = 0x0000; + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Active_EP_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); + await this.driver.request(networkAddress, frame, payload); + const activeEp = await response.start().promise; + debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); + const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); + debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); + const activeEndpoints = [...message[3]]; + + const endpoints = []; + for (const endpoint of activeEndpoints) { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Simple_Desc_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_NONE; + const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpoint); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); + await this.driver.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug('simpleDescriptor got Simple Descriptor payload %O:', message.payload); + const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); + debug('simpleDescriptor got Simple Descriptor parsed: %O',descriptor); + endpoints.push({ + profileID: descriptor[4].profileid, + ID: descriptor[4].endpoint, + deviceID: descriptor[4].deviceid, + inputClusters: descriptor[4].inclusterlist, + outputClusters: descriptor[4].outclusterlist, + }); + } + + return { + networkAddress: networkAddress, + manufacturerID: 0, + ieeeAddr: this.driver.ieee.toString(), + endpoints, + }; + }); } public async permitJoin(seconds: number, networkAddress: number): Promise { @@ -201,7 +246,6 @@ class EZSPAdapter extends Adapter { } public async nodeDescriptor(networkAddress: number): Promise { - // todo try { debug(`Requesting 'Node Descriptor' for '${networkAddress}'`); const result = await this.nodeDescriptorInternal(networkAddress); diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 5847f33d1a..0a15bb944b 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -41,7 +41,7 @@ export class Driver extends EventEmitter { private eui64ToNodeId = new Map(); private pending = new Map>>(); private _nwk: EmberNodeId; - private _ieee: EmberEUI64; + public ieee: EmberEUI64; private _multicast: Multicast; private waitress: Waitress; public queue: Queue; @@ -128,11 +128,11 @@ export class Driver extends EventEmitter { const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; const [ieee] = await this.ezsp.execCommand('getEui64'); - this._ieee = ieee; + this.ieee = ieee; debug.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) - this.handleNodeJoined(nwk, this._ieee, {}, {}, {}); - debug.log(`EZSP nwk=${this._nwk}, IEEE=${this._ieee}`); + this.handleNodeJoined(nwk, this.ieee, {}, {}, {}); + debug.log(`EZSP nwk=${this._nwk}, IEEE=${this.ieee}`); this._multicast = new Multicast(this); await this._multicast.startup([]); From b9b0d1be079811fee555032be1b2e13b75bd7491 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 21 Feb 2021 13:59:09 +0300 Subject: [PATCH 41/63] bind --- src/adapter/ezsp/adapter/ezspAdapter.ts | 28 ++++++++++++++++++++----- src/adapter/ezsp/driver/commands.ts | 6 +++++- src/adapter/ezsp/driver/driver.ts | 4 ++-- src/adapter/ezsp/driver/types/index.ts | 6 +++--- src/adapter/ezsp/driver/types/named.ts | 11 +++++----- src/adapter/ezsp/driver/types/struct.ts | 7 +++++++ 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 3879880474..4f9cc70c98 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -3,13 +3,13 @@ import { NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor, DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters, - StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions, + StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions } from '../../tstype'; import Debug from "debug"; import Adapter from '../../adapter'; const debug = Debug("zigbee-herdsman:adapter:ezsp"); import {Ezsp, Driver} from '../driver'; -import { EmberApsFrame } from '../driver/types/struct'; +import { EmberApsFrame, EmberMultiAddress } from '../driver/types/struct'; import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; @@ -206,7 +206,7 @@ class EZSPAdapter extends Adapter { return { networkAddress: networkAddress, manufacturerID: 0, - ieeeAddr: this.driver.ieee.toString(), + ieeeAddr: `0x${this.driver.ieee.toString()}`, endpoints, }; }); @@ -411,8 +411,26 @@ class EZSPAdapter extends Adapter { clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', destinationEndpoint?: number ): Promise { - // todo - return Promise.reject(); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Bind_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_NONE; + const ieee = new EmberEUI64(sourceIeeeAddress); + const payload = this.driver.make_zdo_frame("Bind_req", frame.sequence, + ieee, sourceEndpoint, clusterID, + {addrmode: 0x03, nwk: destinationNetworkAddress, endpoint: destinationEndpoint} + ); + debug('bind send frame %O:', payload); + const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Bind_rsp); + await this.driver.request(destinationNetworkAddress, frame, payload); + const message = await response.start().promise; + debug('bind got payload %O:', message); + }, destinationNetworkAddress); } public async unbind( diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index feeb040288..d87b5f8e99 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -25,7 +25,7 @@ import {/* Basic Types */ EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, } from './types'; export const COMMANDS = { @@ -701,6 +701,10 @@ export const ZDO_COMMANDS = { "Simple_Desc_rsp": [0x8004, [uint8_t, EmberStatus, EmberNodeId, uint8_t, EmberSimpleDescriptor], []], "Active_EP_req": [0x0005, [uint8_t, EmberNodeId], [EmberStatus]], "Active_EP_rsp": [0x8005, [EmberStatus, uint8_t, EmberNodeId, LVBytes], []], + "Bind_req": [0x0021, [uint8_t, EmberEUI64, uint8_t, uint16_t, EmberMultiAddress], [EmberStatus]], + "Bind_rsp": [0x8021, [EmberStatus], []], + "Unbind_req": [0x0022, [EmberNodeId, uint8_t, uint16_t, EmberMultiAddress], [EmberStatus]], + "Unbind_rsp": [0x8022, [EmberStatus], []], } //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 0a15bb944b..57ee84448f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -128,11 +128,11 @@ export class Driver extends EventEmitter { const [nwk] = await ezsp.execCommand('getNodeId'); this._nwk = nwk; const [ieee] = await this.ezsp.execCommand('getEui64'); - this.ieee = ieee; + this.ieee = new EmberEUI64(ieee); debug.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) this.handleNodeJoined(nwk, this.ieee, {}, {}, {}); - debug.log(`EZSP nwk=${this._nwk}, IEEE=${this.ieee}`); + debug.log(`EZSP nwk=${this._nwk}, IEEE=0x${this.ieee}`); this._multicast = new Multicast(this); await this._multicast.startup([]); diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 418ace9f92..3f43fc3bef 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -21,7 +21,7 @@ import { EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, - EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, + EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, } from './named'; import { @@ -32,7 +32,7 @@ import { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, } from './struct' export function deserialize(payload: any, schema: any[]) { @@ -82,5 +82,5 @@ export { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 0ef79a9a4f..abca5ffd6b 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -31,21 +31,20 @@ export class EmberMulticastId extends basic.uint8_t { export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { - private _str: string; - constructor(private _value: ArrayLike | string) { super() if (typeof (_value) === 'string') { - if ((_value as string).length !== 16) { + if (_value.startsWith('0x')) + _value = _value.slice(2); + if ((_value as string).length !== 16) { throw new Error('Incorrect value passed'); } - this._str = _value; this._value = Buffer.from(_value, 'hex'); } else { if (_value.length !== 8) { throw new Error('Incorrect value passed'); } - this._str = Buffer.from(_value as any).toString('hex'); + this._value = _value; } } @@ -70,7 +69,7 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { } public toString() { - return this._str + return Buffer.from(this._value as any).toString('hex'); } /* diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 07d5a12f7d..0683558c52 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -620,3 +620,10 @@ export class EmberSimpleDescriptor extends EzspStruct { ] } +export class EmberMultiAddress extends EzspStruct { + static _fields = [ + ['addrmode', basic.uint8_t], // 0x3 + ['nwk', named.EmberNodeId], + ['endpoint', basic.uint8_t], + ] +} \ No newline at end of file From 4248455d7eb7d1782e3acbf64799aff3104561e0 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 21 Feb 2021 16:07:43 +0300 Subject: [PATCH 42/63] remove --- src/adapter/ezsp/adapter/ezspAdapter.ts | 20 +++++++++++++++++--- src/adapter/ezsp/driver/commands.ts | 4 +++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 4f9cc70c98..ac03715916 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -442,9 +442,23 @@ class EZSPAdapter extends Adapter { return Promise.reject(); } - public async removeDevice(networkAddress: number, ieeeAddr: string): Promise { - // todo - return Promise.reject(); + public removeDevice(networkAddress: number, ieeeAddr: string): Promise { + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Mgmt_Leave_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_NONE; + const ieee = new EmberEUI64(ieeeAddr); + const payload = this.driver.make_zdo_frame("Mgmt_Leave_req", frame.sequence, ieee, 0x00); + debug('removeDevice send frame %O:', payload); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Mgmt_Leave_rsp); + await this.driver.request(networkAddress, frame, payload); + await response.start().promise; + }, networkAddress); } public async getNetworkParameters(): Promise { diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index d87b5f8e99..d7cdfe9c9d 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -703,8 +703,10 @@ export const ZDO_COMMANDS = { "Active_EP_rsp": [0x8005, [EmberStatus, uint8_t, EmberNodeId, LVBytes], []], "Bind_req": [0x0021, [uint8_t, EmberEUI64, uint8_t, uint16_t, EmberMultiAddress], [EmberStatus]], "Bind_rsp": [0x8021, [EmberStatus], []], - "Unbind_req": [0x0022, [EmberNodeId, uint8_t, uint16_t, EmberMultiAddress], [EmberStatus]], + "Unbind_req": [0x0022, [uint8_t, EmberEUI64, uint8_t, uint16_t, EmberMultiAddress], [EmberStatus]], "Unbind_rsp": [0x8022, [EmberStatus], []], + "Mgmt_Leave_req": [0x0034, [uint8_t, EmberEUI64, uint8_t], [EmberStatus]], + "Mgmt_Leave_rsp": [0x8034, [EmberStatus], []], } //# sourceMappingURL=commands.js.map From d5dc2018707e0cafb30db099a4fbc5c2c4817796 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Sun, 21 Feb 2021 21:38:06 +0300 Subject: [PATCH 43/63] lqi table --- src/adapter/ezsp/adapter/ezspAdapter.ts | 59 +++++++++++++++++++++++-- src/adapter/ezsp/driver/commands.ts | 4 +- src/adapter/ezsp/driver/types/index.ts | 4 +- src/adapter/ezsp/driver/types/struct.ts | 22 ++++++++- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index ac03715916..bb57d20d0f 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -10,7 +10,7 @@ import Adapter from '../../adapter'; const debug = Debug("zigbee-herdsman:adapter:ezsp"); import {Ezsp, Driver} from '../driver'; import { EmberApsFrame, EmberMultiAddress } from '../driver/types/struct'; -import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64 } from '../driver/types'; +import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64, EmberStatus } from '../driver/types'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; import * as Zcl from '../../../zcl'; @@ -236,8 +236,61 @@ class EZSPAdapter extends Adapter { } public async lqi(networkAddress: number): Promise { - // todo - return Promise.reject(); + return this.driver.queue.execute(async (): Promise => { + const neighbors: LQINeighbor[] = []; + + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Mgmt_Lqi_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + const request = async (startIndex: number): Promise => { + frame.sequence = this.nextTransactionID(); + const payload = this.driver.make_zdo_frame("Mgmt_Lqi_req", frame.sequence, startIndex); + const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Mgmt_Lqi_rsp); + await this.driver.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug(`lqi got payload: ${JSON.stringify(message.payload)}`); + const result = this.driver.parse_frame_payload("Mgmt_Lqi_rsp", message.payload); + debug(`lqi got parsed: ${JSON.stringify(result)}`); + + if (result[1] !== EmberStatus.SUCCESS) { + throw new Error(`LQI for '${networkAddress}' failed`); + } + + return result; + }; + + + // eslint-disable-next-line + const add = (list: any) => { + for (const entry of list) { + neighbors.push({ + linkquality: entry.lqi, + networkAddress: entry.nodeid, + ieeeAddr: `0x${new EmberEUI64(entry.ieee).toString()}`, + relationship: (entry.packed >> 4) & 0x7, + depth: entry.depth, + }); + } + }; + + let response = await request(0); + add(response[2].neighbors); + const size = response[2].entries; + let nextStartIndex = response[2].neighbors; + + while (neighbors.length < size) { + response = await request(nextStartIndex); + add(response[2].neighbors); + nextStartIndex += response[2].neighbors.length; + } + + return {neighbors}; + }, networkAddress); } public async routingTable(networkAddress: number): Promise { diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index d7cdfe9c9d..12ddf4cd4a 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -25,7 +25,7 @@ import {/* Basic Types */ EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, } from './types'; export const COMMANDS = { @@ -707,6 +707,8 @@ export const ZDO_COMMANDS = { "Unbind_rsp": [0x8022, [EmberStatus], []], "Mgmt_Leave_req": [0x0034, [uint8_t, EmberEUI64, uint8_t], [EmberStatus]], "Mgmt_Leave_rsp": [0x8034, [EmberStatus], []], + "Mgmt_Lqi_req": [0x0031, [uint8_t, uint8_t], [EmberStatus]], + "Mgmt_Lqi_rsp": [0x8031, [uint8_t, EmberStatus, EmberNeighbors], [EmberStatus]], } //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 3f43fc3bef..f2d1ca921c 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -32,7 +32,7 @@ import { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, } from './struct' export function deserialize(payload: any, schema: any[]) { @@ -82,5 +82,5 @@ export { EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, + EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 0683558c52..a37ccf3a56 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -626,4 +626,24 @@ export class EmberMultiAddress extends EzspStruct { ['nwk', named.EmberNodeId], ['endpoint', basic.uint8_t], ] -} \ No newline at end of file +} + +export class EmberNeighbor extends EzspStruct { + static _fields = [ + ['extendedpanid', basic.fixed_list(8, basic.uint8_t)], + ['ieee', named.EmberEUI64], + ['nodeid', named.EmberNodeId], + ['packed', basic.uint8_t], + ['permitjoining', basic.uint8_t], + ['depth', basic.uint8_t], + ['lqi', basic.uint8_t], + ] +} + +export class EmberNeighbors extends EzspStruct { + static _fields = [ + ['entries', basic.uint8_t], + ['startindex', basic.uint8_t], + ['neighbors', basic.LVList(EmberNeighbor)], + ] +} From 444d13e9af87223a9ae579fd4071d1368bec219c Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 08:30:11 +0300 Subject: [PATCH 44/63] Unbind --- src/adapter/ezsp/adapter/ezspAdapter.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index bb57d20d0f..e8cdd70970 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -491,8 +491,26 @@ class EZSPAdapter extends Adapter { clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group', destinationEndpoint: number ): Promise { - // todo - return Promise.reject(); + return this.driver.queue.execute(async () => { + const frame = new EmberApsFrame(); + frame.clusterId = EmberZDOCmd.Unbind_req; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + frame.options = EmberApsOption.APS_OPTION_NONE; + const ieee = new EmberEUI64(sourceIeeeAddress); + const payload = this.driver.make_zdo_frame("Unbind_req", frame.sequence, + ieee, sourceEndpoint, clusterID, + {addrmode: 0x03, nwk: destinationNetworkAddress, endpoint: destinationEndpoint} + ); + debug('Unbind send frame %O:', payload); + const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Unbind_rsp); + await this.driver.request(destinationNetworkAddress, frame, payload); + const message = await response.start().promise; + debug('Unbind got payload %O:', message); + }, destinationNetworkAddress); } public removeDevice(networkAddress: number, ieeeAddr: string): Promise { From c894fcb88ee3c0e7e966b7e03453e754a2e13797 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 09:03:46 +0300 Subject: [PATCH 45/63] fix bind --- src/adapter/ezsp/adapter/ezspAdapter.ts | 6 ++++-- src/adapter/ezsp/driver/types/struct.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index e8cdd70970..e9e464d2d4 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -474,9 +474,10 @@ class EZSPAdapter extends Adapter { frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_NONE; const ieee = new EmberEUI64(sourceIeeeAddress); + const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); const payload = this.driver.make_zdo_frame("Bind_req", frame.sequence, ieee, sourceEndpoint, clusterID, - {addrmode: 0x03, nwk: destinationNetworkAddress, endpoint: destinationEndpoint} + {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); debug('bind send frame %O:', payload); const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Bind_rsp); @@ -501,9 +502,10 @@ class EZSPAdapter extends Adapter { frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_NONE; const ieee = new EmberEUI64(sourceIeeeAddress); + const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); const payload = this.driver.make_zdo_frame("Unbind_req", frame.sequence, ieee, sourceEndpoint, clusterID, - {addrmode: 0x03, nwk: destinationNetworkAddress, endpoint: destinationEndpoint} + {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); debug('Unbind send frame %O:', payload); const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Unbind_rsp); diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index a37ccf3a56..7bab99749a 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -623,7 +623,7 @@ export class EmberSimpleDescriptor extends EzspStruct { export class EmberMultiAddress extends EzspStruct { static _fields = [ ['addrmode', basic.uint8_t], // 0x3 - ['nwk', named.EmberNodeId], + ['ieee', named.EmberEUI64], ['endpoint', basic.uint8_t], ] } From 16047d5dddd1760f288607ee74d36361c5198011 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 09:42:46 +0300 Subject: [PATCH 46/63] version --- src/adapter/ezsp/adapter/ezspAdapter.ts | 3 +-- src/adapter/ezsp/driver/driver.ts | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index e9e464d2d4..6860eedb03 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -220,7 +220,7 @@ class EZSPAdapter extends Adapter { public async getCoordinatorVersion(): Promise { // todo - return {type: '', meta: {}}; + return {type: 'EmberZNet', meta: this.driver.version}; } public async reset(type: 'soft' | 'hard'): Promise { @@ -264,7 +264,6 @@ class EZSPAdapter extends Adapter { return result; }; - // eslint-disable-next-line const add = (list: any) => { for (const entry of list) { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 57ee84448f..d990ea2192 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -38,6 +38,9 @@ export class Driver extends EventEmitter { public ezsp: Ezsp; private _nwkOpt: TsType.NetworkOptions; public networkParams: EmberNetworkParameters; + public version: { + product: number; majorrel: string; minorrel: string; maintrel: string; revision: string; + }; private eui64ToNodeId = new Map(); private pending = new Map>>(); private _nwk: EmberNodeId; @@ -106,6 +109,7 @@ export class Driver extends EventEmitter { [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); const vers = `${major}.${minor}.${patch}.${special} build ${build}`; debug.log(`EmberZNet version: ${vers}`); + this.version = {product: this.ezsp.ezsp_version, majorrel: `${major}`, minorrel: `${minor}`, maintrel: `${patch} `, revision: vers}; if (await this.needsToBeInitialised(nwkOpt)) { const currentState = await ezsp.execCommand('networkState'); From 7b4dda05d1b26c59529fce41aed2f0f5dc468c3e Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 12:37:50 +0300 Subject: [PATCH 47/63] fix lqi --- src/adapter/ezsp/adapter/ezspAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 6860eedb03..e41427b184 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -280,7 +280,7 @@ class EZSPAdapter extends Adapter { let response = await request(0); add(response[2].neighbors); const size = response[2].entries; - let nextStartIndex = response[2].neighbors; + let nextStartIndex = response[2].neighbors.length; while (neighbors.length < size) { response = await request(nextStartIndex); From 86a8b7161c00ad54e6f9cb23e99a249421348b61 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 14:45:54 +0300 Subject: [PATCH 48/63] restructure zdo requests --- src/adapter/ezsp/adapter/ezspAdapter.ts | 195 +++++------------------- src/adapter/ezsp/driver/driver.ts | 105 +++++-------- src/adapter/ezsp/driver/types/basic.ts | 10 ++ 3 files changed, 85 insertions(+), 225 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index e41427b184..2f743cf29b 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -8,14 +8,11 @@ import { import Debug from "debug"; import Adapter from '../../adapter'; const debug = Debug("zigbee-herdsman:adapter:ezsp"); -import {Ezsp, Driver} from '../driver'; -import { EmberApsFrame, EmberMultiAddress } from '../driver/types/struct'; +import {Driver} from '../driver'; import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64, EmberStatus } from '../driver/types'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; -import * as Zcl from '../../../zcl'; -import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype'; -import {Queue, Waitress, Wait} from '../../../utils'; +import {Waitress} from '../../../utils'; interface WaitressMatcher { @@ -29,13 +26,11 @@ interface WaitressMatcher { class EZSPAdapter extends Adapter { private driver: Driver; private port: SerialPortOptions; - private transactionID: number; private waitress: Waitress; public constructor(networkOptions: NetworkOptions, serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { super(networkOptions, serialPortOptions, backupPath, adapterOptions); - this.transactionID = 1; this.port = serialPortOptions; this.waitress = new Waitress( this.waitressValidator, this.waitressTimeoutFormatter @@ -46,16 +41,6 @@ class EZSPAdapter extends Adapter { this.driver.on('incomingMessage', this.processMessage.bind(this)); } - private nextTransactionID(): number { - this.transactionID++; - - if (this.transactionID > 255) { - this.transactionID = 1; - } - - return this.transactionID; - } - private async processMessage(frame: any) { // todo debug(`processMessage: ${JSON.stringify(frame)}`); @@ -160,40 +145,18 @@ class EZSPAdapter extends Adapter { public async getCoordinator(): Promise { return this.driver.queue.execute(async () => { const networkAddress = 0x0000; - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Active_EP_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); - await this.driver.request(networkAddress, frame, payload); - const activeEp = await response.start().promise; - debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); - const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); - debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); + const message = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Active_EP_req, EmberZDOCmd.Active_EP_rsp, + networkAddress + ); const activeEndpoints = [...message[3]]; const endpoints = []; for (const endpoint of activeEndpoints) { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Simple_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_NONE; - const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpoint); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const message = await response.start().promise; - debug('simpleDescriptor got Simple Descriptor payload %O:', message.payload); - const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); - debug('simpleDescriptor got Simple Descriptor parsed: %O',descriptor); + const descriptor = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Simple_Desc_req, EmberZDOCmd.Simple_Desc_rsp, + networkAddress, endpoint + ); endpoints.push({ profileID: descriptor[4].profileid, ID: descriptor[4].endpoint, @@ -239,24 +202,11 @@ class EZSPAdapter extends Adapter { return this.driver.queue.execute(async (): Promise => { const neighbors: LQINeighbor[] = []; - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Mgmt_Lqi_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; const request = async (startIndex: number): Promise => { - frame.sequence = this.nextTransactionID(); - const payload = this.driver.make_zdo_frame("Mgmt_Lqi_req", frame.sequence, startIndex); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Mgmt_Lqi_rsp); - await this.driver.request(networkAddress, frame, payload); - const message = await response.start().promise; - debug(`lqi got payload: ${JSON.stringify(message.payload)}`); - const result = this.driver.parse_frame_payload("Mgmt_Lqi_rsp", message.payload); - debug(`lqi got parsed: ${JSON.stringify(result)}`); - + const result = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Mgmt_Lqi_req, EmberZDOCmd.Mgmt_Lqi_rsp, + startIndex + ); if (result[1] !== EmberStatus.SUCCESS) { throw new Error(`LQI for '${networkAddress}' failed`); } @@ -310,45 +260,22 @@ class EZSPAdapter extends Adapter { private async nodeDescriptorInternal(networkAddress: number): Promise { return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Node_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Node_Desc_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Node_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const descriptor = await response.start().promise; - debug(`nodeDescriptorInternal got descriptor payload: ${JSON.stringify(descriptor.payload)}`); - const message = this.driver.parse_frame_payload("Node_Desc_rsp", descriptor.payload); - debug(`nodeDescriptorInternal got descriptor parsed: ${message}`); - return {manufacturerCode: message[2].manufacturer_code, type: (message[1] == 0) ? 'Coordinator' : 'EndDevice'}; + const descriptor = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Node_Desc_req, EmberZDOCmd.Node_Desc_rsp, + networkAddress + ); + return {manufacturerCode: descriptor[2].manufacturer_code, type: (descriptor[1] == 0) ? 'Coordinator' : 'EndDevice'}; }); } public async activeEndpoints(networkAddress: number): Promise { - // todo debug(`Requesting 'Active endpoints' for '${networkAddress}'`); return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Active_EP_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - const payload = this.driver.make_zdo_frame("Active_EP_req", frame.sequence, networkAddress); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Active_EP_rsp); - await this.driver.request(networkAddress, frame, payload); - const activeEp = await response.start().promise; - debug(`activeEndpoints got active endpoints payload: ${JSON.stringify(activeEp.payload)}`); - const message = this.driver.parse_frame_payload("Active_EP_rsp", activeEp.payload); - debug(`activeEndpoints got active endpoints parsed: ${JSON.stringify(message)}`); - return {endpoints: [...message[3]]}; + const endpoints = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Active_EP_req, EmberZDOCmd.Active_EP_rsp, + networkAddress + ); + return {endpoints: [...endpoints[3]]}; }, networkAddress); } @@ -356,21 +283,10 @@ class EZSPAdapter extends Adapter { // todo debug(`Requesting 'Simple Descriptor' for '${networkAddress}' endpoint ${endpointID}`); return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Simple_Desc_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_NONE; - const payload = this.driver.make_zdo_frame("Simple_Desc_req", frame.sequence, networkAddress, endpointID); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Simple_Desc_rsp); - await this.driver.request(networkAddress, frame, payload); - const message = await response.start().promise; - debug('simpleDescriptor got Simple Descriptor payload %O:', message.payload); - const descriptor = this.driver.parse_frame_payload("Simple_Desc_rsp", message.payload); - debug('simpleDescriptor got Simple Descriptor parsed: %O',descriptor); + const descriptor = await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Simple_Desc_req, EmberZDOCmd.Simple_Desc_rsp, + networkAddress, endpointID + ); return { profileID: descriptor[4].profileid, endpointID: descriptor[4].endpoint, @@ -416,15 +332,13 @@ class EZSPAdapter extends Adapter { ); } - const frame = new EmberApsFrame(); - frame.clusterId = zclFrame.Cluster.ID; + const frame = this.driver.makeApsFrame(zclFrame.Cluster.ID); frame.profileId = 0x0104; - frame.sequence = this.nextTransactionID(); frame.sourceEndpoint = sourceEndpoint || 0x01; frame.destinationEndpoint = endpoint; frame.groupId = 0; frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - // const response = this.driver.waitFor(networkAddress, ); + const dataConfirmResult = await this.driver.request(networkAddress, frame, zclFrame.toBuffer()); if (response !== null) { @@ -464,25 +378,13 @@ class EZSPAdapter extends Adapter { destinationEndpoint?: number ): Promise { return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Bind_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_NONE; const ieee = new EmberEUI64(sourceIeeeAddress); const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); - const payload = this.driver.make_zdo_frame("Bind_req", frame.sequence, + await this.driver.zdoRequest( + destinationNetworkAddress, EmberZDOCmd.Bind_req, EmberZDOCmd.Bind_rsp, ieee, sourceEndpoint, clusterID, {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); - debug('bind send frame %O:', payload); - const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Bind_rsp); - await this.driver.request(destinationNetworkAddress, frame, payload); - const message = await response.start().promise; - debug('bind got payload %O:', message); }, destinationNetworkAddress); } @@ -492,44 +394,23 @@ class EZSPAdapter extends Adapter { destinationEndpoint: number ): Promise { return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Unbind_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_NONE; const ieee = new EmberEUI64(sourceIeeeAddress); const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); - const payload = this.driver.make_zdo_frame("Unbind_req", frame.sequence, + await this.driver.zdoRequest( + destinationNetworkAddress, EmberZDOCmd.Unbind_req, EmberZDOCmd.Unbind_rsp, ieee, sourceEndpoint, clusterID, {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); - debug('Unbind send frame %O:', payload); - const response = this.driver.waitFor(destinationNetworkAddress, EmberZDOCmd.Unbind_rsp); - await this.driver.request(destinationNetworkAddress, frame, payload); - const message = await response.start().promise; - debug('Unbind got payload %O:', message); }, destinationNetworkAddress); } public removeDevice(networkAddress: number, ieeeAddr: string): Promise { return this.driver.queue.execute(async () => { - const frame = new EmberApsFrame(); - frame.clusterId = EmberZDOCmd.Mgmt_Leave_req; - frame.profileId = 0; - frame.sequence = this.nextTransactionID(); - frame.sourceEndpoint = 0; - frame.destinationEndpoint = 0; - frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_NONE; const ieee = new EmberEUI64(ieeeAddr); - const payload = this.driver.make_zdo_frame("Mgmt_Leave_req", frame.sequence, ieee, 0x00); - debug('removeDevice send frame %O:', payload); - const response = this.driver.waitFor(networkAddress, EmberZDOCmd.Mgmt_Leave_rsp); - await this.driver.request(networkAddress, frame, payload); - await response.start().promise; + await this.driver.zdoRequest( + networkAddress, EmberZDOCmd.Mgmt_Leave_req, EmberZDOCmd.Mgmt_Leave_rsp, + ieee, 0x00 + ); }, networkAddress); } diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index d990ea2192..64e23f1b37 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,13 +1,13 @@ import * as TsType from './../../tstype'; import { Ezsp } from './ezsp'; -import { EzspConfigId, EmberZdoConfigurationFlags, EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t } from './types'; +import { EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; import { EmberObject } from './types/emberObj'; import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType } from './types/named'; import { Multicast } from './multicast'; -import {Queue, Waitress, Wait} from '../../../utils'; +import { Queue, Waitress } from '../../../utils'; import Debug from "debug"; import equals from 'fast-deep-equal/es6'; @@ -48,16 +48,11 @@ export class Driver extends EventEmitter { private _multicast: Multicast; private waitress: Waitress; public queue: Queue; + private transactionID: number; - constructor(){ + constructor() { super(); - - // if (!nodeInfo) return; - - // for(let node of nodeInfo){ - // let eui64 = node.eui64 instanceof EmberEUI64 ? node.eui64 : new EmberEUI64(node.eui64); - // this.eui64ToNodeId.set(eui64.toString(), node.nodeId); - // } + this.transactionID = 1; this.queue = new Queue(); this.waitress = new Waitress( @@ -286,66 +281,40 @@ export class Driver extends EventEmitter { } catch (e) { return false; } - // let seq = apsFrame.sequence+1; + } + + private nextTransactionID(): number { + this.transactionID = (this.transactionID+1) & 0xFF; + return this.transactionID; + } - // console.assert(!this.pending.has(seq)); + public makeApsFrame(clusterId: number) { + const frame = new EmberApsFrame(); + frame.clusterId = clusterId; + frame.profileId = 0; + frame.sequence = this.nextTransactionID(); + frame.sourceEndpoint = 0; + frame.destinationEndpoint = 0; + frame.groupId = 0; + //frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; + frame.options = EmberApsOption.APS_OPTION_NONE; + return frame; + } - - // let sendDeferred = new Deferred(); - // let replyDeferred = new Deferred(); - // this.pending.set(seq, [sendDeferred, replyDeferred]); - - // let handle; - // let eui64: EmberEUI64; - // try { - - // if (timeout > 0) { - // handle = setTimeout(() => { - // throw new Error('Timeout while waiting for reply'); - // }, timeout); - // } - - - // if (typeof nwk !== 'number') { - // eui64 = nwk as EmberEUI64; - // let strEui64 = eui64.toString(); - // let nodeId = this.eui64ToNodeId.get(strEui64); - // if (nodeId === undefined) { - // nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64); - // if (nodeId && nodeId !== 0xFFFF) { - // this.eui64ToNodeId.set(strEui64, nodeId); - // } else { - // throw new Error('Unknown EUI64:' + strEui64); - // } - // } - // nwk = nodeId; - // } else { - // eui64 = await this.networkIdToEUI64(nwk); - // } - // //await this.ezsp.execCommand('setExtendedTimeout', eui64, true); - - // let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); - // console.log('unicast message sent, waiting for reply'); - // if (v[0] != 0) { - // this.pending.delete(seq); - // sendDeferred.reject(false); - // replyDeferred.reject(false); - // throw new Error(`Message send failure ${v[0]}`) - // } - - // await sendDeferred.promise; - // if (timeout > 0) { - // await replyDeferred.promise; - // } else { - // this.pending.delete(seq); - // } - // return true; - // } catch (e) { - // return false; - // } finally { - // if (handle) - // clearTimeout(handle); - // } + public async zdoRequest(networkAddress: number, requestCmd: EmberZDOCmd, responseCmd: EmberZDOCmd, ...args: any[]): Promise { + const requestName = EmberZDOCmd.valueName(EmberZDOCmd, requestCmd); + const responseName = EmberZDOCmd.valueName(EmberZDOCmd, responseCmd); + debug.log(`${requestName} params: ${[...args]}`); + const frame = this.makeApsFrame(requestCmd as number); + const payload = this.make_zdo_frame(requestName, frame.sequence, ...args); + debug.log(`${requestName} frame: ${payload}`); + const response = this.waitFor(networkAddress, responseCmd as number); + await this.request(networkAddress, frame, payload); + const message = await response.start().promise; + debug.log(`${responseName} frame: ${JSON.stringify(message.payload)}`); + const result = this.parse_frame_payload(responseName, message.payload); + debug.log(`${responseName} parsed: ${JSON.stringify(result)}`); + return result; } private handleFrameFailure(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index f5e0ae7a06..c6df6d5fcd 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -24,6 +24,16 @@ export class int_t { }; return ''; } + + static valueName(cls: any, value: any) { + for (let prop of Object.getOwnPropertyNames(cls)) { + const desc = Object.getOwnPropertyDescriptor(cls, prop); + if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { + return `${prop}`; + } + }; + return ''; + } } export class int8s extends int_t { From f6183d54087eb72a39783a92df3759d77b1825a6 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 16:16:42 +0300 Subject: [PATCH 49/63] sequence for waitress zdo request --- src/adapter/ezsp/driver/driver.ts | 41 ++++++++++++++----------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 64e23f1b37..fcecb6aa7a 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -29,23 +29,23 @@ type EmberObjectPayload = any; type EmberWaitressMatcher = { address: number, - clusterId: number + clusterId: number, + sequence: number }; export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT public ezsp: Ezsp; - private _nwkOpt: TsType.NetworkOptions; + private nwkOpt: TsType.NetworkOptions; public networkParams: EmberNetworkParameters; public version: { product: number; majorrel: string; minorrel: string; maintrel: string; revision: string; }; private eui64ToNodeId = new Map(); private pending = new Map>>(); - private _nwk: EmberNodeId; public ieee: EmberEUI64; - private _multicast: Multicast; + private multicast: Multicast; private waitress: Waitress; public queue: Queue; private transactionID: number; @@ -60,11 +60,10 @@ export class Driver extends EventEmitter { } public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions) { - this._nwkOpt = nwkOpt; + this.nwkOpt = nwkOpt; let ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); const version = await ezsp.version(); - // console.log('Got version', version); await ezsp.updateConfig(); @@ -108,7 +107,7 @@ export class Driver extends EventEmitter { if (await this.needsToBeInitialised(nwkOpt)) { const currentState = await ezsp.execCommand('networkState'); - console.log('Network state', currentState); + //console.log('Network state', currentState); if (currentState == EmberNetworkStatus.JOINED_NETWORK) { debug.log(`Leaving current network and forming new network`); const st = await this.ezsp.leaveNetwork(); @@ -125,18 +124,16 @@ export class Driver extends EventEmitter { debug.log("Node type: %s, Network parameters: %s", nodeType, networkParams); const [nwk] = await ezsp.execCommand('getNodeId'); - this._nwk = nwk; const [ieee] = await this.ezsp.execCommand('getEui64'); this.ieee = new EmberEUI64(ieee); debug.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)) this.handleNodeJoined(nwk, this.ieee, {}, {}, {}); - debug.log(`EZSP nwk=${this._nwk}, IEEE=0x${this.ieee}`); + debug.log(`EZSP nwk=${nwk}, IEEE=0x${this.ieee}`); - this._multicast = new Multicast(this); - await this._multicast.startup([]); + this.multicast = new Multicast(this); + await this.multicast.startup([]); } - private async needsToBeInitialised(options: TsType.NetworkOptions): Promise { let valid = true; @@ -156,15 +153,15 @@ export class Driver extends EventEmitter { [status] = await this.ezsp.execCommand('clearKeyTable'); console.assert(status == EmberStatus.SUCCESS); - const panID = this._nwkOpt.panID; - const extendedPanID = this._nwkOpt.extendedPanID; - const initial_security_state:EmberInitialSecurityState = ember_security(this._nwkOpt); + const panID = this.nwkOpt.panID; + const extendedPanID = this.nwkOpt.extendedPanID; + const initial_security_state:EmberInitialSecurityState = ember_security(this.nwkOpt); [status] = await this.ezsp.setInitialSecurityState(initial_security_state); const parameters:EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; parameters.extendedPanId = extendedPanID; parameters.radioTxPower = 20; - parameters.radioChannel = this._nwkOpt.channelList[0]; + parameters.radioChannel = this.nwkOpt.channelList[0]; parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; parameters.nwkManagerId = 0; parameters.nwkUpdateId = 0; @@ -308,7 +305,7 @@ export class Driver extends EventEmitter { const frame = this.makeApsFrame(requestCmd as number); const payload = this.make_zdo_frame(requestName, frame.sequence, ...args); debug.log(`${requestName} frame: ${payload}`); - const response = this.waitFor(networkAddress, responseCmd as number); + const response = this.waitFor(networkAddress, responseCmd as number, frame.sequence); await this.request(networkAddress, frame, payload); const message = await response.start().promise; debug.log(`${responseName} frame: ${JSON.stringify(message.payload)}`); @@ -401,18 +398,18 @@ export class Driver extends EventEmitter { debug.log("Ezsp adding endpoint: %s", res); } - public waitFor(address: number, clusterId: number, timeout: number = 30000) + public waitFor(address: number, clusterId: number, sequence: number, timeout: number = 30000) : {start: () => {promise: Promise; ID: number}; ID: number} { - return this.waitress.waitFor({address, clusterId}, timeout); + return this.waitress.waitFor({address, clusterId, sequence}, timeout); } private waitressTimeoutFormatter(matcher: EmberWaitressMatcher, timeout: number): string { - return `${matcher} after ${timeout}ms`; + return `${JSON.stringify(matcher)} after ${timeout}ms`; } private waitressValidator(payload: EmberObject, matcher: EmberWaitressMatcher): boolean { - const transactionSequenceNumber = payload.frame.sequence; return (!matcher.address || payload.address === matcher.address) && - payload.frame.clusterId === matcher.clusterId; + payload.frame.clusterId === matcher.clusterId && + payload.payload[0] === matcher.sequence; } } \ No newline at end of file From 211e5d590e7b2e2c090218309744b63cdea41f14 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 21:29:46 +0300 Subject: [PATCH 50/63] commands queue --- src/adapter/ezsp/driver/driver.ts | 8 +-- src/adapter/ezsp/driver/ezsp.ts | 113 ++++++++++++++++++------------ 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index fcecb6aa7a..e678767f3f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -103,7 +103,7 @@ export class Driver extends EventEmitter { [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); const vers = `${major}.${minor}.${patch}.${special} build ${build}`; debug.log(`EmberZNet version: ${vers}`); - this.version = {product: this.ezsp.ezsp_version, majorrel: `${major}`, minorrel: `${minor}`, maintrel: `${patch} `, revision: vers}; + this.version = {product: this.ezsp.ezspV, majorrel: `${major}`, minorrel: `${minor}`, maintrel: `${patch} `, revision: vers}; if (await this.needsToBeInitialised(nwkOpt)) { const currentState = await ezsp.execCommand('networkState'); @@ -303,7 +303,7 @@ export class Driver extends EventEmitter { const responseName = EmberZDOCmd.valueName(EmberZDOCmd, responseCmd); debug.log(`${requestName} params: ${[...args]}`); const frame = this.makeApsFrame(requestCmd as number); - const payload = this.make_zdo_frame(requestName, frame.sequence, ...args); + const payload = this.makeZDOframe(requestName, frame.sequence, ...args); debug.log(`${requestName} frame: ${payload}`); const response = this.waitFor(networkAddress, responseCmd as number, frame.sequence); await this.request(networkAddress, frame, payload); @@ -376,8 +376,8 @@ export class Driver extends EventEmitter { return await this.ezsp.execCommand('permitJoining', seconds); } - public make_zdo_frame(name: string, ...args: any[]) { - return this.ezsp.make_zdo_frame(name, ...args); + public makeZDOframe(name: string, ...args: any[]) { + return this.ezsp.makeZDOframe(name, ...args); } public parse_frame_payload(name: string, obj: Buffer) { diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index f3752aea63..2f6ad2dd93 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -6,6 +6,7 @@ import { Deferred } from './utils'; import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags } from './types/named'; import { EventEmitter } from 'events'; import { EmberApsFrame } from './types/struct'; +import { Queue, Waitress } from '../../../utils'; import Debug from "debug"; const debug = { @@ -19,14 +20,25 @@ const MTOR_MAX_INTERVAL = 90; const MTOR_ROUTE_ERROR_THRESHOLD = 4; const MTOR_DELIVERY_FAIL_THRESHOLD = 3; +type EZSPFrame = { + sequence: number, + frameId: number, + frameName: string, + payload: any +}; + +type EZSPWaitressMatcher = { + sequence: number, + frameId: number +}; + export class Ezsp extends EventEmitter { - ezsp_version = 4; - // command sequence - cmdSeq = 0; - _awaiting = new Map }>(); + ezspV = 4; + cmdSeq = 0; // command sequence COMMANDS_BY_ID = new Map(); - _cbCounter = 0; private serialDriver: SerialDriver; + private waitress: Waitress; + private queue: Queue; constructor() { super(); @@ -34,6 +46,9 @@ export class Ezsp extends EventEmitter { let details = (COMMANDS)[name]; this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); } + this.queue = new Queue(); + this.waitress = new Waitress( + this.waitressValidator, this.waitressTimeoutFormatter); this.serialDriver = new SerialDriver(); this.serialDriver.on('received', this.onFrameReceived.bind(this)); @@ -56,7 +71,7 @@ export class Ezsp extends EventEmitter { */ debug.log(`<=== Frame: ${data.toString('hex')}`); var frame_id: number, result, schema, sequence; - if ((this.ezsp_version < 8)) { + if ((this.ezspV < 8)) { [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; } else { sequence = data[0]; @@ -69,44 +84,35 @@ export class Ezsp extends EventEmitter { data = data.slice(2); } } - let cmd = this.COMMANDS_BY_ID.get(frame_id); + const cmd = this.COMMANDS_BY_ID.get(frame_id); if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); - let frameName = cmd.name; + const frameName = cmd.name; debug.log("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); - if (this._awaiting.has(sequence)) { - let entry = this._awaiting.get(sequence); - this._awaiting.delete(sequence); - if (entry) { - console.assert(entry.expectedId === frame_id); - [result, data] = t.deserialize(data, entry.schema); - debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); - entry.deferred.resolve(result); - } - } else { - schema = cmd.outArgs; - frameName = cmd.name; - [result, data] = t.deserialize(data, schema); - debug.log(`<=== Application frame ${frame_id} (${frameName}): ${result}`); - super.emit('frame', frameName, ...result); - } + schema = cmd.outArgs; + [result, data] = t.deserialize(data, schema); + debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); + this.waitress.resolve({frameId: frame_id, frameName: frameName, sequence: sequence, payload: result}); + + this.emit('frame', frameName, ...result); + if ((frame_id === 0)) { - this.ezsp_version = result[0]; + this.ezspV = result[0]; } } async version() { - let version = this.ezsp_version; - let result = await this._command("version", version); + let version = this.ezspV; + let result = await this.command("version", version); if ((result[0] !== version)) { debug.log("Switching to eszp version %d", result[0]); - await this._command("version", result[0]); + await this.command("version", result[0]); } return result[0]; } async networkInit() { let result; - [result] = await this._command("networkInit"); + [result] = await this.command("networkInit"); console.log('network init result', result); return result === EmberStatus.SUCCESS; } @@ -119,7 +125,7 @@ export class Ezsp extends EventEmitter { fut.resolve(response); } }) - v = await this._command("leaveNetwork"); + v = await this.command("leaveNetwork"); if ((v[0] !== EmberStatus.SUCCESS)) { debug.log("Failure to leave network:" + v); throw new Error(("Failure to leave network:" + v)); @@ -262,20 +268,20 @@ export class Ezsp extends EventEmitter { } } - public make_zdo_frame(name: string, ...args: any[]): Buffer { + public makeZDOframe(name: string, ...args: any[]): Buffer { var c, data, frame, cmd_id; c = (ZDO_COMMANDS)[name]; data = t.serialize(args, c[1]); return data; } - private _ezsp_frame(name: string, ...args: any[]) { + private makeFrame(name: string, ...args: any[]) { var c, data, frame, cmd_id; c = (COMMANDS)[name]; data = t.serialize(args, c[1]); frame = [(this.cmdSeq & 255)]; - if ((this.ezsp_version < 8)) { - if ((this.ezsp_version >= 5)) { + if ((this.ezspV < 8)) { + if ((this.ezspV >= 5)) { frame.push(0x00, 0xFF, 0x00, c[0]); } else { frame.push(0x00, c[0]); @@ -287,17 +293,18 @@ export class Ezsp extends EventEmitter { return Buffer.concat([Buffer.from(frame), data]); } - private _command(name: string, ...args: any[]): Promise { - var c, data, deferred; + private command(name: string, ...args: any[]): Promise { debug.log(`===> Send command ${name}: (${args})`); - data = this._ezsp_frame(name, ...args); - debug.log(`===> Send data ${name}: (${data.toString('hex')})`); - this.serialDriver.send(data); - c = (COMMANDS)[name]; - deferred = new Deferred(); - this._awaiting.set(this.cmdSeq, { expectedId: c[0], schema: c[2], deferred }); - this.cmdSeq = (this.cmdSeq + 1 % 256); - return deferred.promise; + return this.queue.execute(async (): Promise => { + const data = this.makeFrame(name, ...args); + debug.log(`===> Send data ${name}: (${data.toString('hex')})`); + const c = (COMMANDS)[name]; + const waiter = this.waitFor(c[0], this.cmdSeq); + this.cmdSeq = (this.cmdSeq + 1 % 256); + this.serialDriver.send(data); + const response = await waiter.start().promise; + return response.payload; + }); } async formNetwork(parameters: {}) { @@ -308,7 +315,7 @@ export class Ezsp extends EventEmitter { fut.resolve(response); } }) - v = await this._command("formNetwork", parameters); + v = await this.command("formNetwork", parameters); if ((v[0] !== EmberStatus.SUCCESS)) { debug.log("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); @@ -325,7 +332,7 @@ export class Ezsp extends EventEmitter { if (Object.keys(COMMANDS).indexOf(name) < 0) { throw new Error('Unknown command: ' + name); } - return this._command(name, ...args); + return this.command(name, ...args); } public parse_frame_payload(name: string, data: Buffer) { @@ -357,4 +364,18 @@ export class Ezsp extends EventEmitter { } await this.execCommand('setSourceRouteDiscoveryMode', 1); } + + public waitFor(frameId: number, sequence: number, timeout: number = 30000) + : {start: () => {promise: Promise; ID: number}; ID: number} { + return this.waitress.waitFor({frameId, sequence}, timeout); + } + + private waitressTimeoutFormatter(matcher: EZSPWaitressMatcher, timeout: number): string { + return `${JSON.stringify(matcher)} after ${timeout}ms`; + } + + private waitressValidator(payload: EZSPFrame, matcher: EZSPWaitressMatcher): boolean { + return (payload.sequence === matcher.sequence && + payload.frameId === matcher.frameId); + } } From e81b24c1e0c7a557fce4a32faff84a798083aeec Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 22 Feb 2021 21:47:34 +0300 Subject: [PATCH 51/63] some cleanup --- src/adapter/ezsp/driver/driver.ts | 18 ++++++++------ src/adapter/ezsp/driver/types/emberObj.ts | 30 ----------------------- 2 files changed, 10 insertions(+), 38 deletions(-) delete mode 100644 src/adapter/ezsp/driver/types/emberObj.ts diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index e678767f3f..2e7d3c3e72 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -3,7 +3,6 @@ import { Ezsp } from './ezsp'; import { EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption } from './types'; import { EventEmitter } from "events"; import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; -import { EmberObject } from './types/emberObj'; import { Deferred, ember_security } from './utils'; import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType } from './types/named'; import { Multicast } from './multicast'; @@ -25,7 +24,11 @@ interface AddEndpointParameters { outputClusters?: number[], }; -type EmberObjectPayload = any; +type EmberFrame = { + address: number; + payload: Buffer; + frame: EmberApsFrame; +} type EmberWaitressMatcher = { address: number, @@ -33,7 +36,6 @@ type EmberWaitressMatcher = { sequence: number }; - export class Driver extends EventEmitter { private direct = EmberOutgoingMessageType.OUTGOING_DIRECT public ezsp: Ezsp; @@ -46,7 +48,7 @@ export class Driver extends EventEmitter { private pending = new Map>>(); public ieee: EmberEUI64; private multicast: Multicast; - private waitress: Waitress; + private waitress: Waitress; public queue: Queue; private transactionID: number; @@ -55,7 +57,7 @@ export class Driver extends EventEmitter { this.transactionID = 1; this.queue = new Queue(); - this.waitress = new Waitress( + this.waitress = new Waitress( this.waitressValidator, this.waitressTimeoutFormatter); } @@ -186,7 +188,7 @@ export class Driver extends EventEmitter { } } - this.waitress.resolve({address: sender, payload: message, frame: apsFrame} as EmberObject); + this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); super.emit('incomingMessage', { messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, @@ -399,7 +401,7 @@ export class Driver extends EventEmitter { } public waitFor(address: number, clusterId: number, sequence: number, timeout: number = 30000) - : {start: () => {promise: Promise; ID: number}; ID: number} { + : {start: () => {promise: Promise; ID: number}; ID: number} { return this.waitress.waitFor({address, clusterId, sequence}, timeout); } @@ -407,7 +409,7 @@ export class Driver extends EventEmitter { return `${JSON.stringify(matcher)} after ${timeout}ms`; } - private waitressValidator(payload: EmberObject, matcher: EmberWaitressMatcher): boolean { + private waitressValidator(payload: EmberFrame, matcher: EmberWaitressMatcher): boolean { return (!matcher.address || payload.address === matcher.address) && payload.frame.clusterId === matcher.clusterId && payload.payload[0] === matcher.sequence; diff --git a/src/adapter/ezsp/driver/types/emberObj.ts b/src/adapter/ezsp/driver/types/emberObj.ts deleted file mode 100644 index 6deebf9206..0000000000 --- a/src/adapter/ezsp/driver/types/emberObj.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './struct'; - - -export class EmberObject { - private readonly _address: number; - private readonly _payload: Buffer; - private readonly _frame: EmberApsFrame; - - private constructor( - address: number, - frame: EmberApsFrame, - payload: Buffer - ) { - this._address = address; - this._payload = payload; - this._frame = frame; - } - - get address(): number { - return this._address; - } - - get frame(): EmberApsFrame { - return this._frame; - } - - get payload(): Buffer { - return this._payload; - } -} From de04735de222e22f02b55a8360cae780fd50b6a0 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 23 Feb 2021 20:07:59 +0300 Subject: [PATCH 52/63] some logs and restructure --- src/adapter/ezsp/driver/driver.ts | 144 ++++++++++++------------------ src/adapter/ezsp/driver/ezsp.ts | 2 +- src/adapter/ezsp/driver/uart.ts | 61 ++++++------- 3 files changed, 85 insertions(+), 122 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 2e7d3c3e72..1243116c69 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -45,7 +45,7 @@ export class Driver extends EventEmitter { product: number; majorrel: string; minorrel: string; maintrel: string; revision: string; }; private eui64ToNodeId = new Map(); - private pending = new Map>>(); + private eui64ToRelays = new Map(); public ieee: EmberEUI64; private multicast: Multicast; private waitress: Waitress; @@ -109,16 +109,16 @@ export class Driver extends EventEmitter { if (await this.needsToBeInitialised(nwkOpt)) { const currentState = await ezsp.execCommand('networkState'); - //console.log('Network state', currentState); + debug.log('Network state', currentState); if (currentState == EmberNetworkStatus.JOINED_NETWORK) { debug.log(`Leaving current network and forming new network`); const st = await this.ezsp.leaveNetwork(); console.assert(st == EmberStatus.NETWORK_DOWN); } await this.form_network(); - const state = await ezsp.execCommand('networkState'); - debug.log('Network state', state); } + const state = await ezsp.execCommand('networkState'); + debug.log('Network state', state); let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); console.assert(status == EmberStatus.SUCCESS); @@ -177,44 +177,66 @@ export class Driver extends EventEmitter { } private handleFrame(frameName: string, ...args: any[]) { - - if (frameName === 'incomingMessageHandler') { - let [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; - let eui64; - for(let [k,v] of this.eui64ToNodeId){ - if (v === sender){ - eui64 = k; - break; + switch (true) { + case (frameName === 'incomingMessageHandler'): { + let [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; + let eui64; + for(let [k,v] of this.eui64ToNodeId){ + if (v === sender){ + eui64 = k; + break; + } } - } - - this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); - super.emit('incomingMessage', { - messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, - senderEui64: eui64 - }); + this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); - let isReply = false; - let tsn = -1; - let commandId = 0; - if (isReply) { - this.handleReply(sender, apsFrame, tsn, commandId, args); + super.emit('incomingMessage', { + messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, + senderEui64: eui64 + }); + break; } - } else if (frameName === 'messageSentHandler') { - if (args[4] != 0) { - this.handleFrameFailure.apply(this, args); - } else { - this.handleFrameSent.apply(this, args); + case (frameName === 'trustCenterJoinHandler'): { + if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { + this.handleNodeLeft.apply(this, args); + } else { + this.handleNodeJoined.apply(this, args); + } + break; } - } else if (frameName === 'trustCenterJoinHandler') { - if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { - this.handleNodeLeft.apply(this, args); - } else { - this.handleNodeJoined.apply(this, args); + case (frameName === 'incomingRouteRecordHandler'): { + const [nwk, ieee, lqi, rssi, relays] = args; + this.handleRouteRecord(nwk, ieee, lqi, rssi, relays); + break; + } + case (frameName === 'incomingRouteErrorHandler'): { + const [status, nwk] = args; + this.handleRouteError(status, nwk); + break; } + case (frameName === 'messageSentHandler'): { + // todo + const status = args[4]; + if (status != 0) { + // send failure + } else { + // send success + } + break; + } + default: + debug.log(`Unhandled frame ${frameName}`); } + } + + private handleRouteRecord(nwk: number, ieee: EmberEUI64 | number[], lqi: number, rssi: number, relays: any) { + debug.log(`handleRouteRecord: nwk=${nwk}, ieee=${ieee}, lqi=${lqi}, rssi=${rssi}, relays=${relays}`); + // todo + } + private handleRouteError(status: EmberStatus, nwk: number) { + debug.log(`handleRouteError: number=${status}, nwk=${nwk}`); + // todo } private handleNodeLeft(nwk: number, ieee: EmberEUI64 | number[], ...args: any[]) { @@ -233,25 +255,6 @@ export class Driver extends EventEmitter { this.emit('deviceJoined', [nwk, ieee]); } - private handleReply(sender: number, apsFrame: EmberApsFrame, tsn: number, commandId: number, ...args: any[]) { - try { - var arr = this.pending.get(tsn); - if (!arr) { - debug.log('Unexpected reponse TSN=', tsn, 'command=', commandId, args) - return; - }; - let [sendDeferred, replyDeferred] = arr; - if (sendDeferred.isFullfilled) { - this.pending.delete(tsn); - } - replyDeferred.resolve(args); - return; - } catch (e) { - debug.log(e); - return; - } - } - public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { try { let seq = apsFrame.sequence+1; @@ -316,41 +319,6 @@ export class Driver extends EventEmitter { return result; } - private handleFrameFailure(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { - try { - var arr = this.pending.get(messageTag); - if (!arr) { - console.log("Unexpected message send failure"); - return; - } - this.pending.delete(messageTag); - let [sendDeferred,] = arr; - let e = new Error('Message send failure:' + status); - console.log(e); - sendDeferred.reject(e); - //replyDeferred.reject(e); - } catch (e) { - console.log(e); - } - } - - private handleFrameSent(messageType: number, destination: number, apsFrame: EmberApsFrame, messageTag: number, status: number, message: Buffer) { - try { - var arr = this.pending.get(messageTag); - if (!arr) { - console.log("Unexpected message send notification"); - return; - } - let [sendDeferred, replyDeferred] = arr; - if (replyDeferred.isFullfilled) { - this.pending.delete(messageTag); - } - sendDeferred.resolve(true); - } catch (e) { - console.log(e); - } - } - public stop() { return this.ezsp.close(); } diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 2f6ad2dd93..0f09f67cff 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -301,7 +301,7 @@ export class Ezsp extends EventEmitter { const c = (COMMANDS)[name]; const waiter = this.waitFor(c[0], this.cmdSeq); this.cmdSeq = (this.cmdSeq + 1 % 256); - this.serialDriver.send(data); + this.serialDriver.sendDATA(data); const response = await waiter.start().promise; return response.payload; }); diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 2366d18d44..7a11e2f830 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -240,32 +240,27 @@ export class SerialDriver extends EventEmitter { }); } - // private showCounters(frmNum?: number, ackNum?: number) { - // //debug.log(`send [${this.sendSeq}:${(frmNum==undefined) ? ' ' : frmNum}][${this.recvSeq}:${(ackNum==undefined) ? ' ' : ackNum}] recv `); - // debug(`[${this.sendSeq}:${this.ackSeq}|${this.recvSeq}]`); - // } - private onParsed(data: Buffer): void { try { /* Frame receive handler */ switch (true) { case ((data[0] & 0x80) === 0): - debug(`Data frame : ${data.toString('hex')}`); - this.data_frame_received(data); + debug(`Recv DATA frame (${(data[0] & 0x70) >> 4},${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); + this.handleDATA(data); break; case ((data[0] & 0xE0) === 0x80): - debug(`ACK frame : ${data.toString('hex')}`); - this.handle_ack(data[0]); + debug(`Recv ACK frame (${data[0] & 0x07}): ${data.toString('hex')}`); + this.handleACK(data[0]); break; case ((data[0] & 0xE0) === 0xA0): - debug(`NAK frame : ${data.toString('hex')}`); - this.handle_nak(data[0]); + debug(`Recv NAK frame (${data[0] & 0x07}): ${data.toString('hex')}`); + this.handleNAK(data[0]); break; case (data[0] === 0xC0): - debug(`RST frame : ${data.toString('hex')}`); + debug(`Recv RST frame: ${data.toString('hex')}`); break; case (data[0] === 0xC1): @@ -285,28 +280,26 @@ export class SerialDriver extends EventEmitter { } } - private data_frame_received(data: Buffer) { + private handleDATA(data: Buffer) { /* Data frame receive handler */ - const seq = ((data[0] & 0x70) >> 4); + const frmNum = (data[0] & 0x70) >> 4; + const ackNum = data[0] & 0x07; + const reTx = (data[0] & 0x08) >> 3; // if (seq !== this.recvSeq) { // debug('NAK-NAK'); // } - this.recvSeq = (seq + 1) & 7; // next - const ack_frame = this.make_ack_frame(); - this.handle_ack(data[0]); - - debug(`Write ack`); - this.writer.writeBuffer(ack_frame); - + this.recvSeq = (frmNum + 1) & 7; // next + this.sendACK(this.recvSeq); + this.handleACK(data[0]); data = data.slice(1, (- 3)); const frame = this.randomize(data); this.emit('received', frame); } - private handle_ack(control: number) { + private handleACK(control: number) { /* Handle an acknowledgement frame */ // next number after the last accepted frame - this.ackSeq = control & 7; + this.ackSeq = control & 0x07; // var ack, pending; // ack = (((control & 7) - 1) % 8); // if ((ack === this._pending[0])) { @@ -315,9 +308,9 @@ export class SerialDriver extends EventEmitter { // } } - private handle_nak(control: number) { + private handleNAK(control: number) { /* Handle negative acknowledgment frame */ - // let nak = (control & 7); + const nakNum = control & 0x07; // if ((nak === this._pending[0])) { // this._pending[1].set_result(false); // } @@ -344,11 +337,6 @@ export class SerialDriver extends EventEmitter { this.resetDeferred.resolve(true); } - private make_ack_frame(): Buffer { - /* Construct a acknowledgement frame */ - return this.make_frame([(0b10000000 | this.recvSeq)]); - } - private make_frame(control: ArrayLike, data?: ArrayLike): Buffer { /* Construct a frame */ const ctrlArr: Array = Array.from(control); @@ -433,16 +421,23 @@ export class SerialDriver extends EventEmitter { return this.initialized; } - - public send(data: Buffer) { + private sendACK(ackNum: number) { + /* Construct a acknowledgement frame */ + const ackFrame = this.make_frame([(0b10000000 | ackNum)]); + debug(`Send ACK frame (${ackNum})`); + this.writer.writeBuffer(ackFrame); + } + + public sendDATA(data: Buffer) { let seq = this.sendSeq; this.sendSeq = ((seq + 1) % 8); // next - debug(`Write frame (${seq}): ${data.toString('hex')}`); let pack; try { + debug(`Send DATA frame (${seq},${this.recvSeq},0): ${data.toString('hex')}`); pack = this.data_frame(data, seq, 0); this.writer.writeBuffer(pack); } catch (e) { + debug(`Send DATA frame (${seq},${this.recvSeq},1): ${data.toString('hex')}`); pack = this.data_frame(data, seq, 1); this.writer.writeBuffer(pack); } From fc4c15aa0cd4515457bf2a9ae05c1294fea5d4f8 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 23 Feb 2021 23:14:32 +0300 Subject: [PATCH 53/63] waitress resolve result --- src/adapter/ezsp/driver/driver.ts | 22 ++++++++-------------- src/adapter/ezsp/driver/ezsp.ts | 4 ++-- src/utils/waitress.ts | 5 ++++- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 1243116c69..19f420ce27 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -180,20 +180,15 @@ export class Driver extends EventEmitter { switch (true) { case (frameName === 'incomingMessageHandler'): { let [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; - let eui64; - for(let [k,v] of this.eui64ToNodeId){ - if (v === sender){ - eui64 = k; - break; - } + const eui64 = this.eui64ToNodeId.get(sender); + + const handled = this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); + if (!handled) { + this.emit('incomingMessage', { + messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, + senderEui64: eui64 + }); } - - this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); - - super.emit('incomingMessage', { - messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, - senderEui64: eui64 - }); break; } case (frameName === 'trustCenterJoinHandler'): { @@ -278,7 +273,6 @@ export class Driver extends EventEmitter { await this.ezsp.execCommand('setExtendedTimeout', eui64, true); let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); - debug.log('unicast message sent, waiting for reply'); return true; } catch (e) { return false; diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 0f09f67cff..64a9fbd99e 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -91,9 +91,9 @@ export class Ezsp extends EventEmitter { schema = cmd.outArgs; [result, data] = t.deserialize(data, schema); debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); - this.waitress.resolve({frameId: frame_id, frameName: frameName, sequence: sequence, payload: result}); + const handled = this.waitress.resolve({frameId: frame_id, frameName: frameName, sequence: sequence, payload: result}); - this.emit('frame', frameName, ...result); + if (!handled) this.emit('frame', frameName, ...result); if ((frame_id === 0)) { this.ezspV = result[0]; diff --git a/src/utils/waitress.ts b/src/utils/waitress.ts index ac8079aeef..5932f3de6a 100644 --- a/src/utils/waitress.ts +++ b/src/utils/waitress.ts @@ -25,7 +25,8 @@ class Waitress { this.currentID = 0; } - public resolve(payload: TPayload): void { + public resolve(payload: TPayload): boolean { + let result = false; for (const entry of this.waiters.entries()) { const index = entry[0]; const waiter = entry[1]; @@ -36,8 +37,10 @@ class Waitress { waiter.resolved = true; waiter.resolve(payload); this.waiters.delete(index); + result = true; } } + return result; } public remove(ID: number): void { From 6e1674eaa7b68f4db12e97cd4e89fff7fb6f84e5 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Wed, 24 Feb 2021 18:17:08 +0300 Subject: [PATCH 54/63] uart queue and waiting --- src/adapter/ezsp/driver/uart.ts | 88 +++++++++++++++++++++++++++++---- src/utils/waitress.ts | 18 +++++++ 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 7a11e2f830..b554362a72 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -4,6 +4,7 @@ import net from 'net'; import SocketPortUtils from '../../socketPortUtils'; import { Deferred, crc16ccitt } from './utils'; import * as stream from 'stream'; +import { Queue, Waitress } from '../../../utils'; import Debug from "debug"; const debug = Debug('zigbee-herdsman:adapter:ezsp:uart'); @@ -132,6 +133,14 @@ class Writer extends stream.Readable { } } +type EZSPPacket = { + sequence: number +}; + +type EZSPPacketMatcher = { + sequence: number +}; + export class SerialDriver extends EventEmitter { private serialPort: SerialPort; @@ -144,10 +153,15 @@ export class SerialDriver extends EventEmitter { private sendSeq: number = 0; // next frame number to send private recvSeq: number = 0; // next frame number to receive private ackSeq: number = 0; // next number after the last accepted frame + private waitress: Waitress; + private queue: Queue; constructor(){ super(); this.initialized = false; + this.queue = new Queue(); + this.waitress = new Waitress( + this.waitressValidator, this.waitressTimeoutFormatter); } async connect(path: string, options: {}) { @@ -300,6 +314,12 @@ export class SerialDriver extends EventEmitter { /* Handle an acknowledgement frame */ // next number after the last accepted frame this.ackSeq = control & 0x07; + const handled = this.waitress.resolve({sequence: this.ackSeq}); + if (!handled) { + debug(`Unexpected packet sequence ${this.ackSeq}`); + } else { + debug(`Expected packet sequence ${this.ackSeq}`); + } // var ack, pending; // ack = (((control & 7) - 1) % 8); // if ((ack === this._pending[0])) { @@ -311,6 +331,13 @@ export class SerialDriver extends EventEmitter { private handleNAK(control: number) { /* Handle negative acknowledgment frame */ const nakNum = control & 0x07; + const handled = this.waitress.reject({sequence: nakNum}, 'Recv NAK frame'); + if (!handled) { + // send NAK + debug(`NAK Unexpected packet sequence ${nakNum}`); + } else { + debug(`NAK Expected packet sequence ${nakNum}`); + } // if ((nak === this._pending[0])) { // this._pending[1].set_result(false); // } @@ -367,9 +394,9 @@ export class SerialDriver extends EventEmitter { return out; } - private data_frame(data: Buffer, seq: number, rxmit: number) { + private makeDataFrame(data: Buffer, seq: number, rxmit: number, ackSeq: number) { /* Construct a data frame */ - const control = (((seq << 4) | (rxmit << 3)) | this.recvSeq); + const control = (((seq << 4) | (rxmit << 3)) | ackSeq); return this.make_frame([control], this.randomize(data)); } @@ -431,15 +458,56 @@ export class SerialDriver extends EventEmitter { public sendDATA(data: Buffer) { let seq = this.sendSeq; this.sendSeq = ((seq + 1) % 8); // next + const nextSeq = this.sendSeq; + const ackSeq = this.recvSeq; let pack; - try { - debug(`Send DATA frame (${seq},${this.recvSeq},0): ${data.toString('hex')}`); - pack = this.data_frame(data, seq, 0); - this.writer.writeBuffer(pack); - } catch (e) { - debug(`Send DATA frame (${seq},${this.recvSeq},1): ${data.toString('hex')}`); - pack = this.data_frame(data, seq, 1); + + return this.queue.execute(async (): Promise => { + debug(`Send DATA frame (${seq},${ackSeq},0): ${data.toString('hex')}`); + pack = this.makeDataFrame(data, seq, 0, ackSeq); + const waiter = this.waitFor(nextSeq); + debug(`waiting (${nextSeq})`); this.writer.writeBuffer(pack); - } + await waiter.start().promise.catch(async (e) => { + debug(`break waiting (${nextSeq})`); + debug(`Can't send DATA frame (${seq},${ackSeq},0): ${data.toString('hex')}`); + debug(`Resend DATA frame (${seq},${ackSeq},1): ${data.toString('hex')}`); + pack = this.makeDataFrame(data, seq, 1, ackSeq); + const waiter = this.waitFor(nextSeq); + debug(`rewaiting (${nextSeq})`); + this.writer.writeBuffer(pack); + await waiter.start().promise.catch((e) => { + debug(`break rewaiting (${nextSeq})`); + debug(`Can't send DATA frame (${seq},${ackSeq},0): ${data.toString('hex')}`); + throw new Error(`sendDATA error: ${e}`); + }); + debug(`rewaiting (${nextSeq}) success`); + }); + debug(`waiting (${nextSeq}) success`); + }); + + + // try { + // debug(`Send DATA frame (${seq},${this.recvSeq},0): ${data.toString('hex')}`); + // pack = this.data_frame(data, seq, 0); + // this.writer.writeBuffer(pack); + // } catch (e) { + // debug(`Send DATA frame (${seq},${this.recvSeq},1): ${data.toString('hex')}`); + // pack = this.data_frame(data, seq, 1); + // this.writer.writeBuffer(pack); + // } + } + + public waitFor(sequence: number, timeout: number = 10000) + : {start: () => {promise: Promise; ID: number}; ID: number} { + return this.waitress.waitFor({sequence}, timeout); + } + + private waitressTimeoutFormatter(matcher: EZSPPacketMatcher, timeout: number): string { + return `${JSON.stringify(matcher)} after ${timeout}ms`; + } + + private waitressValidator(payload: EZSPPacket, matcher: EZSPPacketMatcher): boolean { + return (payload.sequence === matcher.sequence); } } \ No newline at end of file diff --git a/src/utils/waitress.ts b/src/utils/waitress.ts index 5932f3de6a..c9da44d114 100644 --- a/src/utils/waitress.ts +++ b/src/utils/waitress.ts @@ -54,6 +54,24 @@ class Waitress { } } + public reject(payload: TPayload, message: string): boolean { + let result = false; + for (const entry of this.waiters.entries()) { + const index = entry[0]; + const waiter = entry[1]; + if (waiter.timedout) { + this.waiters.delete(index); + } else if (this.validator(payload, waiter.matcher)) { + clearTimeout(waiter.timer); + waiter.resolved = true; + this.waiters.delete(index); + waiter.reject(new Error(message)); + result = true; + } + } + return result; + } + public waitFor( matcher: TMatcher, timeout: number ): {ID: number; start: () => {promise: Promise; ID: number}} { From 086c5be7b5b5a29c52707cf4df5c7eedf2cbf236 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Wed, 24 Feb 2021 20:55:48 +0300 Subject: [PATCH 55/63] fix networkInit proc --- src/adapter/ezsp/driver/ezsp.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 64a9fbd99e..f05de54296 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -111,10 +111,22 @@ export class Ezsp extends EventEmitter { } async networkInit() { - let result; - [result] = await this.command("networkInit"); - console.log('network init result', result); - return result === EmberStatus.SUCCESS; + var fut: Deferred, v, st; + fut = new Deferred(); + this.on('frame', (frameName: string, response: any) => { + if ((frameName === "stackStatusHandler")) { + fut.resolve(response); + } + }) + + const [result] = await this.command("networkInit"); + debug.log('network init result', result); + if ((result !== EmberStatus.SUCCESS)) { + debug.log("Failure to init network:" + result); + throw new Error(("Failure to init network:" + result)); + } + v = await fut.promise; + return (v === EmberStatus.NETWORK_UP); } async leaveNetwork() { From c29e67f56cbdb8625be748468ecd3038aadedaa3 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Thu, 25 Feb 2021 22:48:56 +0300 Subject: [PATCH 56/63] small init fix. when network was down --- src/adapter/ezsp/driver/ezsp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index f05de54296..5f11ad2d10 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -123,7 +123,7 @@ export class Ezsp extends EventEmitter { debug.log('network init result', result); if ((result !== EmberStatus.SUCCESS)) { debug.log("Failure to init network:" + result); - throw new Error(("Failure to init network:" + result)); + return false; } v = await fut.promise; return (v === EmberStatus.NETWORK_UP); From 7ffd300715d18e733e53ec4d95bf5ee2429aed75 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 26 Feb 2021 07:51:39 +0300 Subject: [PATCH 57/63] eslint fix --- src/adapter/ezsp/driver/commands.ts | 36 +- src/adapter/ezsp/driver/driver.ts | 128 +- src/adapter/ezsp/driver/ezsp.ts | 52 +- src/adapter/ezsp/driver/index.ts | 6 +- src/adapter/ezsp/driver/multicast.ts | 16 +- src/adapter/ezsp/driver/types/basic.ts | 60 +- src/adapter/ezsp/driver/types/index.ts | 12 +- src/adapter/ezsp/driver/types/named.ts | 1162 +++++++++---------- src/adapter/ezsp/driver/types/struct.ts | 94 +- src/adapter/ezsp/driver/uart.ts | 100 +- src/adapter/ezsp/driver/utils/crc16ccitt.ts | 10 +- src/adapter/ezsp/driver/utils/index.ts | 42 +- 12 files changed, 859 insertions(+), 859 deletions(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 12ddf4cd4a..074e16c0a6 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -11,11 +11,11 @@ import {/* Basic Types */ EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberNodeId, EmberPanId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, EmberGpKeyType, SecureEzspRandomNumber, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, - EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, + EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EzspNetworkScanType, EmberJoinDecision, EmberKeyType, EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, - EzspZllNetworkOperation, + EzspZllNetworkOperation, /* Structs */ EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, @@ -39,16 +39,16 @@ export const COMMANDS = { [EzspStatus] ], "addEndpoint": [0x0002, [ - uint8_t, - uint16_t, - uint16_t, - uint8_t, - uint8_t, - uint8_t, - WordList, - WordList, - ], - [EzspStatus], + uint8_t, + uint16_t, + uint16_t, + uint8_t, + uint8_t, + uint8_t, + WordList, + WordList, + ], + [EzspStatus], ], "setPolicy": [85, [EzspPolicyId, EzspDecisionId], [EzspStatus] @@ -83,7 +83,7 @@ export const COMMANDS = { "setChannelMap": [247, [uint8_t, uint8_t], [] ], - "nop": [5, Array(), + "nop": [5, [], [] ], "echo": [129, [LVBytes], @@ -92,10 +92,10 @@ export const COMMANDS = { "invalidCommand": [88, [], [EzspStatus] ], - "callback": [6, Array(), + "callback": [6, [], [] ], - "noCallbacks": [7, Array(), + "noCallbacks": [7, [], [] ], "setToken": [9, [uint8_t, fixed_list(8, uint8_t)], @@ -404,7 +404,7 @@ export const COMMANDS = { "addTransientLinkKey": [175, [EmberEUI64, EmberKeyData], [EmberStatus] ], - "clearTransientLinkKeys": [107, Array(), + "clearTransientLinkKeys": [107, [], [] ], "setSecurityKey": [202, [EmberKeyData, SecureEzspSecurityType], @@ -590,7 +590,7 @@ export const COMMANDS = { "zllSetDataToken": [189, [EmberTokTypeStackZllData], [] ], - "zllSetNonZllNetwork": [191, Array(), + "zllSetNonZllNetwork": [191, [], [] ], "isZllNetwork": [190, [], @@ -709,6 +709,6 @@ export const ZDO_COMMANDS = { "Mgmt_Leave_rsp": [0x8034, [EmberStatus], []], "Mgmt_Lqi_req": [0x0031, [uint8_t, uint8_t], [EmberStatus]], "Mgmt_Lqi_rsp": [0x8031, [uint8_t, EmberStatus, EmberNeighbors], [EmberStatus]], -} +}; //# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 19f420ce27..30c66f5be8 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,12 +1,12 @@ import * as TsType from './../../tstype'; -import { Ezsp } from './ezsp'; -import { EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption } from './types'; -import { EventEmitter } from "events"; -import { EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState } from './types/struct'; -import { Deferred, ember_security } from './utils'; -import { EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType } from './types/named'; -import { Multicast } from './multicast'; -import { Queue, Waitress } from '../../../utils'; +import {Ezsp} from './ezsp'; +import {EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption} from './types'; +import {EventEmitter} from "events"; +import {EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState} from './types/struct'; +import {Deferred, ember_security} from './utils'; +import {EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType} from './types/named'; +import {Multicast} from './multicast'; +import {Queue, Waitress} from '../../../utils'; import Debug from "debug"; import equals from 'fast-deep-equal/es6'; @@ -22,13 +22,13 @@ interface AddEndpointParameters { appFlags?: number, inputClusters?: number[], outputClusters?: number[], -}; +} type EmberFrame = { address: number; payload: Buffer; frame: EmberApsFrame; -} +}; type EmberWaitressMatcher = { address: number, @@ -37,7 +37,7 @@ type EmberWaitressMatcher = { }; export class Driver extends EventEmitter { - private direct = EmberOutgoingMessageType.OUTGOING_DIRECT + private direct = EmberOutgoingMessageType.OUTGOING_DIRECT; public ezsp: Ezsp; private nwkOpt: TsType.NetworkOptions; public networkParams: EmberNetworkParameters; @@ -63,7 +63,7 @@ export class Driver extends EventEmitter { public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions) { this.nwkOpt = nwkOpt; - let ezsp = this.ezsp = new Ezsp(); + const ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); const version = await ezsp.version(); @@ -120,7 +120,7 @@ export class Driver extends EventEmitter { const state = await ezsp.execCommand('networkState'); debug.log('Network state', state); - let [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); + const [status, nodeType, networkParams] = await ezsp.execCommand('getNetworkParameters'); console.assert(status == EmberStatus.SUCCESS); this.networkParams = networkParams; debug.log("Node type: %s, Network parameters: %s", nodeType, networkParams); @@ -129,7 +129,7 @@ export class Driver extends EventEmitter { const [ieee] = await this.ezsp.execCommand('getEui64'); this.ieee = new EmberEUI64(ieee); debug.log('Network ready'); - ezsp.on('frame', this.handleFrame.bind(this)) + ezsp.on('frame', this.handleFrame.bind(this)); this.handleNodeJoined(nwk, this.ieee, {}, {}, {}); debug.log(`EZSP nwk=${nwk}, IEEE=0x${this.ieee}`); @@ -140,7 +140,7 @@ export class Driver extends EventEmitter { private async needsToBeInitialised(options: TsType.NetworkOptions): Promise { let valid = true; valid = valid && (await this.ezsp.networkInit()); - let [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); + const [status, nodeType, networkParams] = await this.ezsp.execCommand('getNetworkParameters'); debug.log("Current Node type: %s, Network parameters: %s", nodeType, networkParams); valid = valid && (status == EmberStatus.SUCCESS); valid = valid && (nodeType == EmberNodeType.COORDINATOR); @@ -178,49 +178,49 @@ export class Driver extends EventEmitter { private handleFrame(frameName: string, ...args: any[]) { switch (true) { - case (frameName === 'incomingMessageHandler'): { - let [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; - const eui64 = this.eui64ToNodeId.get(sender); - - const handled = this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); - if (!handled) { - this.emit('incomingMessage', { - messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, - senderEui64: eui64 - }); - } - break; - } - case (frameName === 'trustCenterJoinHandler'): { - if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { - this.handleNodeLeft.apply(this, args); - } else { - this.handleNodeJoined.apply(this, args); - } - break; - } - case (frameName === 'incomingRouteRecordHandler'): { - const [nwk, ieee, lqi, rssi, relays] = args; - this.handleRouteRecord(nwk, ieee, lqi, rssi, relays); - break; + case (frameName === 'incomingMessageHandler'): { + const [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; + const eui64 = this.eui64ToNodeId.get(sender); + + const handled = this.waitress.resolve({address: sender, payload: message, frame: apsFrame}); + if (!handled) { + this.emit('incomingMessage', { + messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message, + senderEui64: eui64 + }); } - case (frameName === 'incomingRouteErrorHandler'): { - const [status, nwk] = args; - this.handleRouteError(status, nwk); - break; + break; + } + case (frameName === 'trustCenterJoinHandler'): { + if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { + this.handleNodeLeft.apply(this, args); + } else { + this.handleNodeJoined.apply(this, args); } - case (frameName === 'messageSentHandler'): { - // todo - const status = args[4]; - if (status != 0) { - // send failure - } else { - // send success - } - break; + break; + } + case (frameName === 'incomingRouteRecordHandler'): { + const [nwk, ieee, lqi, rssi, relays] = args; + this.handleRouteRecord(nwk, ieee, lqi, rssi, relays); + break; + } + case (frameName === 'incomingRouteErrorHandler'): { + const [status, nwk] = args; + this.handleRouteError(status, nwk); + break; + } + case (frameName === 'messageSentHandler'): { + // todo + const status = args[4]; + if (status != 0) { + // send failure + } else { + // send success } - default: - debug.log(`Unhandled frame ${frameName}`); + break; + } + default: + debug.log(`Unhandled frame ${frameName}`); } } @@ -239,7 +239,7 @@ export class Driver extends EventEmitter { ieee = new EmberEUI64(ieee); } this.eui64ToNodeId.delete(ieee.toString()); - this.emit('deviceLeft', [nwk, ieee]) + this.emit('deviceLeft', [nwk, ieee]); } private handleNodeJoined(nwk: number, ieee: EmberEUI64 | number[], deviceUpdate: any, joinDec: any, parentNwk: any) { @@ -252,11 +252,11 @@ export class Driver extends EventEmitter { public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { try { - let seq = apsFrame.sequence+1; + const seq = apsFrame.sequence+1; let eui64: EmberEUI64; if (typeof nwk !== 'number') { eui64 = nwk as EmberEUI64; - let strEui64 = eui64.toString(); + const strEui64 = eui64.toString(); let nodeId = this.eui64ToNodeId.get(strEui64); if (nodeId === undefined) { nodeId = await this.ezsp.execCommand('lookupNodeIdByEui64', eui64); @@ -272,7 +272,7 @@ export class Driver extends EventEmitter { } await this.ezsp.execCommand('setExtendedTimeout', eui64, true); - let v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + const v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); return true; } catch (e) { return false; @@ -322,16 +322,16 @@ export class Driver extends EventEmitter { } public async networkIdToEUI64(nwk: number): Promise { - for (let [eUI64, value] of this.eui64ToNodeId) { + for (const [eUI64, value] of this.eui64ToNodeId) { if (value === nwk) return new EmberEUI64(eUI64); } - let value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); + const value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); if (value[0] === EmberStatus.SUCCESS) { - let eUI64 = new EmberEUI64(value[1] as any); + const eUI64 = new EmberEUI64(value[1] as any); this.eui64ToNodeId.set(eUI64.toString(), nwk); return eUI64; } else { - throw new Error('Unrecognized nodeId:' + nwk) + throw new Error('Unrecognized nodeId:' + nwk); } } @@ -362,7 +362,7 @@ export class Driver extends EventEmitter { debug.log("Ezsp adding endpoint: %s", res); } - public waitFor(address: number, clusterId: number, sequence: number, timeout: number = 30000) + public waitFor(address: number, clusterId: number, sequence: number, timeout = 30000) : {start: () => {promise: Promise; ID: number}; ID: number} { return this.waitress.waitFor({address, clusterId, sequence}, timeout); } diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 5f11ad2d10..af961df36e 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,12 +1,12 @@ import * as t from './types'; -import { SerialDriver } from './uart'; -import { COMMANDS, ZDO_COMMANDS } from './commands'; - -import { Deferred } from './utils'; -import { EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags } from './types/named'; -import { EventEmitter } from 'events'; -import { EmberApsFrame } from './types/struct'; -import { Queue, Waitress } from '../../../utils'; +import {SerialDriver} from './uart'; +import {COMMANDS, ZDO_COMMANDS} from './commands'; + +import {Deferred} from './utils'; +import {EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags} from './types/named'; +import {EventEmitter} from 'events'; +import {EmberApsFrame} from './types/struct'; +import {Queue, Waitress} from '../../../utils'; import Debug from "debug"; const debug = { @@ -42,9 +42,9 @@ export class Ezsp extends EventEmitter { constructor() { super(); - for (let name in COMMANDS) { - let details = (COMMANDS)[name]; - this.COMMANDS_BY_ID.set(details[0], { name, inArgs: details[1], outArgs: details[2] }); + for (const name in COMMANDS) { + const details = (COMMANDS)[name]; + this.COMMANDS_BY_ID.set(details[0], {name, inArgs: details[1], outArgs: details[2]}); } this.queue = new Queue(); this.waitress = new Waitress( @@ -70,7 +70,7 @@ export class Ezsp extends EventEmitter { data randomization removed. */ debug.log(`<=== Frame: ${data.toString('hex')}`); - var frame_id: number, result, schema, sequence; + let frame_id: number, result, schema, sequence; if ((this.ezspV < 8)) { [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; } else { @@ -101,8 +101,8 @@ export class Ezsp extends EventEmitter { } async version() { - let version = this.ezspV; - let result = await this.command("version", version); + const version = this.ezspV; + const result = await this.command("version", version); if ((result[0] !== version)) { debug.log("Switching to eszp version %d", result[0]); await this.command("version", result[0]); @@ -111,13 +111,13 @@ export class Ezsp extends EventEmitter { } async networkInit() { - var fut: Deferred, v, st; + let fut: Deferred, v, st; fut = new Deferred(); this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); } - }) + }); const [result] = await this.command("networkInit"); debug.log('network init result', result); @@ -130,13 +130,13 @@ export class Ezsp extends EventEmitter { } async leaveNetwork() { - var fut: Deferred, v, st; + let fut: Deferred, v, st; fut = new Deferred(); this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); } - }) + }); v = await this.command("leaveNetwork"); if ((v[0] !== EmberStatus.SUCCESS)) { debug.log("Failure to leave network:" + v); @@ -254,7 +254,7 @@ export class Ezsp extends EventEmitter { [EzspConfigId.CONFIG_PACKET_BUFFER_COUNT, 255], ]; - for (let [confName, value] of config) { + for (const [confName, value] of config) { await this.setConfigurationValue(confName, value); } } @@ -275,20 +275,20 @@ export class Ezsp extends EventEmitter { [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], ]; - for (let [policy, value] of policies) { + for (const [policy, value] of policies) { await this.setPolicy(policy, value); } } public makeZDOframe(name: string, ...args: any[]): Buffer { - var c, data, frame, cmd_id; + let c, data, frame, cmd_id; c = (ZDO_COMMANDS)[name]; data = t.serialize(args, c[1]); return data; } private makeFrame(name: string, ...args: any[]) { - var c, data, frame, cmd_id; + let c, data, frame, cmd_id; c = (COMMANDS)[name]; data = t.serialize(args, c[1]); frame = [(this.cmdSeq & 255)]; @@ -320,13 +320,13 @@ export class Ezsp extends EventEmitter { } async formNetwork(parameters: {}) { - var fut: Deferred, v, st; + let fut: Deferred, v, st; fut = new Deferred(); this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); } - }) + }); v = await this.command("formNetwork", parameters); if ((v[0] !== EmberStatus.SUCCESS)) { debug.log("Failure forming network:" + v); @@ -369,7 +369,7 @@ export class Ezsp extends EventEmitter { MTOR_ROUTE_ERROR_THRESHOLD, MTOR_DELIVERY_FAIL_THRESHOLD, 0, - ) + ); debug.log("Set concentrator type: %s", res); if (res != EmberStatus.SUCCESS) { debug.log("Couldn't set concentrator type %s: %s", true, res); @@ -377,7 +377,7 @@ export class Ezsp extends EventEmitter { await this.execCommand('setSourceRouteDiscoveryMode', 1); } - public waitFor(frameId: number, sequence: number, timeout: number = 30000) + public waitFor(frameId: number, sequence: number, timeout = 30000) : {start: () => {promise: Promise; ID: number}; ID: number} { return this.waitress.waitFor({frameId, sequence}, timeout); } diff --git a/src/adapter/ezsp/driver/index.ts b/src/adapter/ezsp/driver/index.ts index bbf9987ce9..bc64193aca 100644 --- a/src/adapter/ezsp/driver/index.ts +++ b/src/adapter/ezsp/driver/index.ts @@ -1,3 +1,3 @@ -import { Ezsp } from './ezsp'; -import { Driver } from './driver'; -export { Ezsp, Driver } \ No newline at end of file +import {Ezsp} from './ezsp'; +import {Driver} from './driver'; +export {Ezsp, Driver}; \ No newline at end of file diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 5f00a8cfa0..98d2ad2709 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -1,8 +1,8 @@ -import { Driver } from './driver'; -import { EzspConfigId, EmberZdoConfigurationFlags } from './types'; +import {Driver} from './driver'; +import {EzspConfigId, EmberZdoConfigurationFlags} from './types'; import * as t from './types/basic'; -import { EmberStatus, EmberOutgoingMessageType, EmberMulticastId } from './types/named'; -import { EmberMulticastTableEntry } from './types/struct'; +import {EmberStatus, EmberOutgoingMessageType, EmberMulticastId} from './types/named'; +import {EmberMulticastTableEntry} from './types/struct'; import Debug from "debug"; const debug = { @@ -45,9 +45,9 @@ export class Multicast { async startup(enpoints: Array) { return this.driver.queue.execute(async () => { await this._initialize(); - for (let ep of enpoints) { + for (const ep of enpoints) { if (!ep.id) continue; - for (let group_id of ep.member_of) { + for (const group_id of ep.member_of) { await this.subscribe(group_id); } } @@ -73,7 +73,7 @@ export class Multicast { idx, entry.multicastId, status, - ) + ); this._available.push(idx); return status; } @@ -84,7 +84,7 @@ export class Multicast { idx, entry.multicastId, status, - ) + ); return status; } catch (e) { debug.log("No more available slots MulticastId subscription"); diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index c6df6d5fcd..b84399773b 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -1,8 +1,8 @@ export class int_t { - static _signed = true + static _signed = true; static serialize(cls: any, value: number) { - let buffer = Buffer.alloc(cls._size, 0); + const buffer = Buffer.alloc(cls._size, 0); if (cls._signed) { buffer.writeIntLE(value, 0, cls._size); } else { @@ -12,72 +12,72 @@ export class int_t { } static deserialize(cls: any, data: Buffer) { - return [cls._signed ? data.readIntLE(0, cls._size) : data.readUIntLE(0, cls._size), data.slice(cls._size)] + return [cls._signed ? data.readIntLE(0, cls._size) : data.readUIntLE(0, cls._size), data.slice(cls._size)]; } static valueToName(cls: any, value: any) { - for (let prop of Object.getOwnPropertyNames(cls)) { + for (const prop of Object.getOwnPropertyNames(cls)) { const desc = Object.getOwnPropertyDescriptor(cls, prop); if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { return `${cls.name}.${prop}`; } - }; + } return ''; } static valueName(cls: any, value: any) { - for (let prop of Object.getOwnPropertyNames(cls)) { + for (const prop of Object.getOwnPropertyNames(cls)) { const desc = Object.getOwnPropertyDescriptor(cls, prop); if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { return `${prop}`; } - }; + } return ''; } } export class int8s extends int_t { - static _size = 1 + static _size = 1; } export class int16s extends int_t { - static _size = 2 + static _size = 2; } export class int24s extends int_t { - static _size = 3 + static _size = 3; } export class int32s extends int_t { - static _size = 4 + static _size = 4; } export class int64s extends int_t { - static _size = 8 + static _size = 8; } export class uint_t extends int_t { - static _signed = false + static _signed = false; } export class uint8_t extends uint_t { - static _size = 1 + static _size = 1; } export class uint16_t extends uint_t { - static _size = 2 + static _size = 2; } export class uint24_t extends uint_t { - static _size = 3 + static _size = 3; } export class uint32_t extends uint_t { - static _size = 4 + static _size = 4; } export class uint64_t extends uint_t { - static _size = 8 + static _size = 8; } /* @@ -101,14 +101,14 @@ export class Double extends number { export class LVBytes { static serialize(cls: any, value: any[]) { if (Buffer.isBuffer(value)) { - var ret = Buffer.alloc(1); + const ret = Buffer.alloc(1); ret.writeUInt8(value.length, 0); return Buffer.concat([ret, value]); } return Buffer.from([value.length].concat(value)); } static deserialize(cls: any, data: Buffer) { - var l, s; + let l, s; l = data.readIntLE(0, 1); s = data.slice(1, (l + 1)); return [s, data.slice((l + 1))]; @@ -121,8 +121,8 @@ export abstract class List { return Buffer.from(value.map(i => i.serialize(cls, i))); } static deserialize(cls: any, data: Buffer): (any[] | Buffer)[] { - var item; - var r: any[] = []; + let item; + const r: any[] = []; while (data) { [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); @@ -133,16 +133,16 @@ export abstract class List { class _LVList extends List { static serialize(cls: any, value: any[]) { - var data, head; + let data, head; head = [cls.length]; data = super.serialize(cls, value); return Buffer.from(head.concat(data)); } static deserialize(cls: any, data: Buffer) { - var item, length; - var r: any[] = []; + let item, length; + const r: any[] = []; [length, data] = [data[0], data.slice(1)]; - for (var i = 0; i < length; i++) { + for (let i = 0; i < length; i++) { [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); } @@ -151,14 +151,14 @@ class _LVList extends List { } export function list(itemtype: any) : List { class ConreteList extends List { - static itemtype = itemtype + static itemtype = itemtype; } return ConreteList; } export function LVList(itemtype: any) : List { class LVList extends _LVList { - static itemtype = itemtype + static itemtype = itemtype; } return LVList; } @@ -177,8 +177,8 @@ class _FixedList extends List { } static deserialize(cls: any, data: Buffer) { let item; - let r: any[] = []; - for (var i = 0; i < cls._length; i++) { + const r: any[] = []; + for (let i = 0; i < cls._length; i++) { [item, data] = cls.itemtype.deserialize(cls.itemtype, data); r.push(item); } diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index f2d1ca921c..bfdf2d1196 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -33,14 +33,14 @@ import { EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, -} from './struct' +} from './struct'; export function deserialize(payload: any, schema: any[]) { - let result = []; + const result = []; let value, data = payload; - for (let type of schema) { - [value, data] = type.deserialize(type, data) - result.push(value) + for (const type of schema) { + [value, data] = type.deserialize(type, data); + result.push(value); } return [result, data]; } @@ -83,4 +83,4 @@ export { EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index abca5ffd6b..b9a196d18d 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -1,19 +1,19 @@ import * as basic from './basic'; -import { fixed_list } from './basic'; +import {fixed_list} from './basic'; console.assert(basic.uint8_t); export class NcpResetCode extends basic.uint8_t { //Reset and Error Codes for NCP - static RESET_UNKNOWN_REASON = 0x00 - static RESET_EXTERNAL = 0x01 - static RESET_POWER_ON = 0x02 - static RESET_WATCHDOG = 0x03 - static RESET_ASSERT = 0x06 - static RESET_BOOTLOADER = 0x09 - static RESET_SOFTWARE = 0x0B - static ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51 - static ERROR_UNKNOWN_EM3XX_ERROR = 0x80 + static RESET_UNKNOWN_REASON = 0x00; + static RESET_EXTERNAL = 0x01; + static RESET_POWER_ON = 0x02; + static RESET_WATCHDOG = 0x03; + static RESET_ASSERT = 0x06; + static RESET_BOOTLOADER = 0x09; + static RESET_SOFTWARE = 0x0B; + static ERROR_EXCEEDED_MAXIMUM_ACK_TIMEOUT_COUNT = 0x51; + static ERROR_UNKNOWN_EM3XX_ERROR = 0x80; } export class EmberRf4ceTxOption extends basic.uint8_t { @@ -32,7 +32,7 @@ export class EmberMulticastId extends basic.uint8_t { export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { constructor(private _value: ArrayLike | string) { - super() + super(); if (typeof (_value) === 'string') { if (_value.startsWith('0x')) _value = _value.slice(2); @@ -49,8 +49,8 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { } static deserialize(cls: any, data: Buffer) { - var r; - var arr = super.deserialize(cls, data); + let r; + const arr = super.deserialize(cls, data); r = arr[0]; data = arr[1] as Buffer; return [(r as number[]).reverse(), data]; @@ -93,8 +93,8 @@ export class SecureEzspRandomNumber extends basic.uint64_t { export class SecureEzspSessionId extends basic.uint64_t { } export class Bool extends basic.uint8_t { - static false = 0x00 // An alias for zero, used for clarity. - static true = 0x01 // An alias for one, used for clarity. + static false = 0x00; // An alias for zero, used for clarity. + static true = 0x01; // An alias for one, used for clarity. } export class EzspConfigId extends basic.uint8_t { @@ -104,61 +104,61 @@ export class EzspConfigId extends basic.uint8_t { // special value 0xFF, the NCP will allocate all remaining configuration RAM // towards packet buffers, such that the resulting count will be the largest // whole number of packet buffers that can fit into the available memory. - static CONFIG_PACKET_BUFFER_COUNT = 0x01 + static CONFIG_PACKET_BUFFER_COUNT = 0x01; // The maximum number of router neighbors the stack can keep track of. A // neighbor is a node within radio range. - static CONFIG_NEIGHBOR_TABLE_SIZE = 0x02 + static CONFIG_NEIGHBOR_TABLE_SIZE = 0x02; // The maximum number of APS retried messages the stack can be transmitting // at any time. - static CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03 + static CONFIG_APS_UNICAST_MESSAGE_COUNT = 0x03; // The maximum number of non-volatile bindings supported by the stack. - static CONFIG_BINDING_TABLE_SIZE = 0x04 + static CONFIG_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 ::CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE). - static CONFIG_ADDRESS_TABLE_SIZE = 0x05 + static CONFIG_ADDRESS_TABLE_SIZE = 0x05; // The maximum number of multicast groups that the device may be a member // of. - static CONFIG_MULTICAST_TABLE_SIZE = 0x06 + static CONFIG_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. - static CONFIG_ROUTE_TABLE_SIZE = 0x07 + static CONFIG_ROUTE_TABLE_SIZE = 0x07; // The number of simultaneous route discoveries that a node will support. - static CONFIG_DISCOVERY_TABLE_SIZE = 0x08 + static CONFIG_DISCOVERY_TABLE_SIZE = 0x08; // The size of the alarm broadcast buffer. - static CONFIG_BROADCAST_ALARM_DATA_SIZE = 0x09 + static CONFIG_BROADCAST_ALARM_DATA_SIZE = 0x09; // The size of the unicast alarm buffers allocated for end device children. - static CONFIG_UNICAST_ALARM_DATA_SIZE = 0x0A + static CONFIG_UNICAST_ALARM_DATA_SIZE = 0x0A; // Specifies the stack profile. - static CONFIG_STACK_PROFILE = 0x0C + static CONFIG_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). - static CONFIG_SECURITY_LEVEL = 0x0D + static CONFIG_SECURITY_LEVEL = 0x0D; // The maximum number of hops for a message. - static CONFIG_MAX_HOPS = 0x10 + static CONFIG_MAX_HOPS = 0x10; // The maximum number of end device children that a router will support. - static CONFIG_MAX_END_DEVICE_CHILDREN = 0x11 + static CONFIG_MAX_END_DEVICE_CHILDREN = 0x11; // The maximum amount of time that the MAC will hold a message for indirect // transmission to a child. - static CONFIG_INDIRECT_TRANSMISSION_TIMEOUT = 0x12 + static CONFIG_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. - static CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13 + static CONFIG_END_DEVICE_POLL_TIMEOUT = 0x13; // The maximum amount of time that a mobile node can wait between polls. If // no poll is heard within this timeout, then the parent removes the mobile // node from its tables. - static CONFIG_MOBILE_NODE_POLL_TIMEOUT = 0x14 + static CONFIG_MOBILE_NODE_POLL_TIMEOUT = 0x14; // The number of child table entries reserved for use only by mobile nodes. - static CONFIG_RESERVED_MOBILE_CHILD_ENTRIES = 0x15 + static CONFIG_RESERVED_MOBILE_CHILD_ENTRIES = 0x15; // Enables boost power mode and/or the alternate transmitter output. - static CONFIG_TX_POWER_MODE = 0x17 + static CONFIG_TX_POWER_MODE = 0x17; // 0: Allow this node to relay messages. 1: Prevent this node from relaying // messages. - static CONFIG_DISABLE_RELAY = 0x18 + static CONFIG_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 @@ -169,34 +169,34 @@ export class EzspConfigId extends basic.uint8_t { // the total number of such address associations maintained by the NCP is // the sum of the value of this setting and the value of // ::CONFIG_ADDRESS_TABLE_SIZE.) - static CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19 + static CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE = 0x19; // The size of the source route table. - static CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A + static CONFIG_SOURCE_ROUTE_TABLE_SIZE = 0x1A; // The units used for timing out end devices on their parents. - static CONFIG_END_DEVICE_POLL_TIMEOUT_SHIFT = 0x1B + static CONFIG_END_DEVICE_POLL_TIMEOUT_SHIFT = 0x1B; // The number of blocks of a fragmented message that can be sent in a single // window. - static CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C + static CONFIG_FRAGMENT_WINDOW_SIZE = 0x1C; // The time the stack will wait (in milliseconds) between sending blocks of // a fragmented message. - static CONFIG_FRAGMENT_DELAY_MS = 0x1D + static CONFIG_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). - static CONFIG_KEY_TABLE_SIZE = 0x1E + static CONFIG_KEY_TABLE_SIZE = 0x1E; // The APS ACK timeout value. The stack waits this amount of time between // resends of APS retried messages. - static CONFIG_APS_ACK_TIMEOUT = 0x1F + static CONFIG_APS_ACK_TIMEOUT = 0x1F; // The duration of an active scan, in the units used by the 15.4 scan // parameter (((1 << duration) + 1) * 15ms). This also controls the jitter // used when responding to a beacon request. - static CONFIG_ACTIVE_SCAN_DURATION = 0x20 + static CONFIG_ACTIVE_SCAN_DURATION = 0x20; // The time the coordinator will wait (in seconds) for a second end device // bind request to arrive. - static CONFIG_END_DEVICE_BIND_TIMEOUT = 0x21 + static CONFIG_END_DEVICE_BIND_TIMEOUT = 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. - static CONFIG_PAN_ID_CONFLICT_REPORT_THRESHOLD = 0x22 + static CONFIG_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 @@ -204,12 +204,12 @@ export class EzspConfigId extends basic.uint8_t { // 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. - static CONFIG_REQUEST_KEY_TIMEOUT = 0x24 + static CONFIG_REQUEST_KEY_TIMEOUT = 0x24; // 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. - static CONFIG_CERTIFICATE_TABLE_SIZE = 0x29 + static CONFIG_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 @@ -217,34 +217,34 @@ export class EzspConfigId extends basic.uint8_t { // application must check the APS options bitfield within the // incomingMessageHandler callback to see if the // APS_OPTION_ZDO_RESPONSE_REQUIRED flag is set. - static CONFIG_APPLICATION_ZDO_FLAGS = 0x2A + static CONFIG_APPLICATION_ZDO_FLAGS = 0x2A; // The maximum number of broadcasts during a single broadcast timeout // period. - static CONFIG_BROADCAST_TABLE_SIZE = 0x2B + static CONFIG_BROADCAST_TABLE_SIZE = 0x2B; // The size of the MAC filter list table. - static CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C + static CONFIG_MAC_FILTER_TABLE_SIZE = 0x2C; // The number of supported networks. - static CONFIG_SUPPORTED_NETWORKS = 0x2D + static CONFIG_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. - static CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E + static CONFIG_SEND_MULTICASTS_TO_SLEEPY_ADDRESS = 0x2E; // ZLL group address initial configuration. - static CONFIG_ZLL_GROUP_ADDRESSES = 0x2F + static CONFIG_ZLL_GROUP_ADDRESSES = 0x2F; // ZLL rssi threshold initial configuration. - static CONFIG_ZLL_RSSI_THRESHOLD = 0x30 + static CONFIG_ZLL_RSSI_THRESHOLD = 0x30; // The maximum number of pairings supported by the stack. Controllers // must support at least one pairing table entry while targets must // support at least five. - static CONFIG_RF4CE_PAIRING_TABLE_SIZE = 0x31 + static CONFIG_RF4CE_PAIRING_TABLE_SIZE = 0x31; // The maximum number of outgoing RF4CE packets supported by the stack. - static CONFIG_RF4CE_PENDING_OUTGOING_PACKET_TABLE_SIZE = 0x32 + static CONFIG_RF4CE_PENDING_OUTGOING_PACKET_TABLE_SIZE = 0x32; // Toggles the mtorr flow control in the stack. - static CONFIG_MTORR_FLOW_CONTROL = 0x33 + static CONFIG_MTORR_FLOW_CONTROL = 0x33; // Setting the retry queue size. - static CONFIG_RETRY_QUEUE_SIZE = 0x34 + static CONFIG_RETRY_QUEUE_SIZE = 0x34; // Setting the new broadcast entry threshold. - static CONFIG_NEW_BROADCAST_ENTRY_THRESHOLD = 0x35 + static CONFIG_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 @@ -252,15 +252,15 @@ export class EzspConfigId extends basic.uint8_t { // 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. - static CONFIG_TRANSIENT_KEY_TIMEOUT_S = 0x36 + static CONFIG_TRANSIENT_KEY_TIMEOUT_S = 0x36; // The number of passive acknowledgements to record from neighbors before we stop // re-transmitting broadcasts - static CONFIG_BROADCAST_MIN_ACKS_NEEDED = 0x37 + static CONFIG_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 emAllowTcRejoinsUsingWellKnownKeyTimeoutSec value. - static CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38 + static CONFIG_TC_REJOINS_USING_WELL_KNOWN_KEY_TIMEOUT_S = 0x38; } @@ -268,51 +268,51 @@ export class EzspValueId extends basic.uint8_t { // Identifies a value. // The contents of the node data stack token. - static VALUE_TOKEN_STACK_NODE_DATA = 0x00 + static VALUE_TOKEN_STACK_NODE_DATA = 0x00; // The types of MAC passthrough messages that the host wishes to receive. - static VALUE_MAC_PASSTHROUGH_FLAGS = 0x01 + static VALUE_MAC_PASSTHROUGH_FLAGS = 0x01; // The source address used to filter legacy EmberNet messages when the // MAC_PASSTHROUGH_EMBERNET_SOURCE flag is set in // VALUE_MAC_PASSTHROUGH_FLAGS. - static VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02 + static VALUE_EMBERNET_PASSTHROUGH_SOURCE_ADDRESS = 0x02; // The number of available message buffers. - static VALUE_FREE_BUFFERS = 0x03 + static VALUE_FREE_BUFFERS = 0x03; // Selects sending synchronous callbacks in ezsp-uart. - static VALUE_UART_SYNCH_CALLBACKS = 0x04 + static VALUE_UART_SYNCH_CALLBACKS = 0x04; // The maximum incoming transfer size for the local node. - static VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05 + static VALUE_MAXIMUM_INCOMING_TRANSFER_SIZE = 0x05; // The maximum outgoing transfer size for the local node. - static VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06 + static VALUE_MAXIMUM_OUTGOING_TRANSFER_SIZE = 0x06; // A boolean indicating whether stack tokens are written to persistent // storage as they change. - static VALUE_STACK_TOKEN_WRITING = 0x07 + static VALUE_STACK_TOKEN_WRITING = 0x07; // A read-only value indicating whether the stack is currently performing a // rejoin. - static VALUE_STACK_IS_PERFORMING_REJOIN = 0x08 + static VALUE_STACK_IS_PERFORMING_REJOIN = 0x08; // A list of EmberMacFilterMatchData values. - static VALUE_MAC_FILTER_LIST = 0x09 + static VALUE_MAC_FILTER_LIST = 0x09; // The Ember Extended Security Bitmask. - static VALUE_EXTENDED_SECURITY_BITMASK = 0x0A + static VALUE_EXTENDED_SECURITY_BITMASK = 0x0A; // The node short ID. - static VALUE_NODE_SHORT_ID = 0x0B + static VALUE_NODE_SHORT_ID = 0x0B; // The descriptor capability of the local node. - static VALUE_DESCRIPTOR_CAPABILITY = 0x0C + static VALUE_DESCRIPTOR_CAPABILITY = 0x0C; // The stack device request sequence number of the local node. - static VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D + static VALUE_STACK_DEVICE_REQUEST_SEQUENCE_NUMBER = 0x0D; // Enable or disable radio hold-off. - static VALUE_RADIO_HOLD_OFF = 0x0E + static VALUE_RADIO_HOLD_OFF = 0x0E; // The flags field associated with the endpoint data. - static VALUE_ENDPOINT_FLAGS = 0x0F + static VALUE_ENDPOINT_FLAGS = 0x0F; // Enable/disable the Mfg security config key settings. - static VALUE_MFG_SECURITY_CONFIG = 0x10 + static VALUE_MFG_SECURITY_CONFIG = 0x10; // Retrieves the version information from the stack on the NCP. - static VALUE_VERSION_INFO = 0x11 + static VALUE_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 // REJOIN_REASON_NONE. The NCP will store the rejoin reason used by the // call to emberFindAndRejoinNetwork() - static VALUE_NEXT_HOST_REJOIN_REASON = 0x12 + static VALUE_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 @@ -322,59 +322,59 @@ export class EzspValueId extends basic.uint8_t { // 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(VALUE_REAL_REJOIN_REASON). - static VALUE_LAST_REJOIN_REASON = 0x13 + static VALUE_LAST_REJOIN_REASON = 0x13; // The next ZigBee sequence number. - static VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14 + static VALUE_NEXT_ZIGBEE_SEQUENCE_NUMBER = 0x14; // CCA energy detect threshold for radio. - static VALUE_CCA_THRESHOLD = 0x15 + static VALUE_CCA_THRESHOLD = 0x15; // The threshold value for a counter - static VALUE_SET_COUNTER_THRESHOLD = 0x17 + static VALUE_SET_COUNTER_THRESHOLD = 0x17; // Resets all counters thresholds to 0xFF - static VALUE_RESET_COUNTER_THRESHOLDS = 0x18 + static VALUE_RESET_COUNTER_THRESHOLDS = 0x18; // Clears all the counters - static VALUE_CLEAR_COUNTERS = 0x19 + static VALUE_CLEAR_COUNTERS = 0x19; // The device RF4CE base channel - static VALUE_RF4CE_BASE_CHANNEL = 0x1A + static VALUE_RF4CE_BASE_CHANNEL = 0x1A; // The RF4CE device types supported by the node - static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST = 0x1B + static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST = 0x1B; // The RF4CE profiles supported by the node - static VALUE_RF4CE_SUPPORTED_PROFILES_LIST = 0x1C + static VALUE_RF4CE_SUPPORTED_PROFILES_LIST = 0x1C; // Setting this byte enables R21 behavior on the NCP. - static VALUE_ENABLE_R21_BEHAVIOR = 0x29 + static VALUE_ENABLE_R21_BEHAVIOR = 0x29; // Configure the antenna mode(0-primary,1-secondary,2- toggle on tx ack // fail). - static VALUE_ANTENNA_MODE = 0x30 + static VALUE_ANTENNA_MODE = 0x30; // The GDP binding recipient parameters - static VALUE_RF4CE_GDP_BINDING_RECIPIENT_PARAMETERS = 0x1D + static VALUE_RF4CE_GDP_BINDING_RECIPIENT_PARAMETERS = 0x1D; // The GDP binding push button stimulus received pending flag - static VALUE_RF4CE_GDP_PUSH_BUTTON_STIMULUS_RECEIVED_PENDING_FLAG = 0x1E + static VALUE_RF4CE_GDP_PUSH_BUTTON_STIMULUS_RECEIVED_PENDING_FLAG = 0x1E; // The GDP originator proxy flag in the advanced binding options - static VALUE_RF4CE_GDP_BINDING_PROXY_FLAG = 0x1F + static VALUE_RF4CE_GDP_BINDING_PROXY_FLAG = 0x1F; // The GDP application specific user s join unti_VALUE_RF4CE_MSO_USER_STRING // 0x21 The MSO user string - static VALUE_RF4CE_GDP_APPLICATION_SPECIFIC_USER_STRING = 0x20 + static VALUE_RF4CE_GDP_APPLICATION_SPECIFIC_USER_STRING = 0x20; // The MSO user string - static VALUE_RF4CE_MSO_USER_STRING = 0x21 + static VALUE_RF4CE_MSO_USER_STRING = 0x21; // The MSO binding recipient parameters - static VALUE_RF4CE_MSO_BINDING_RECIPIENT_PARAMETERS = 0x22 + static VALUE_RF4CE_MSO_BINDING_RECIPIENT_PARAMETERS = 0x22; // The NWK layer security frame counter value - static VALUE_NWK_FRAME_COUNTER = 0x23 + static VALUE_NWK_FRAME_COUNTER = 0x23; // The APS layer security frame counter value - static VALUE_APS_FRAME_COUNTER = 0x24 + static VALUE_APS_FRAME_COUNTER = 0x24; // Sets the device type to use on the next rejoin using device type - static VALUE_RETRY_DEVICE_TYPE = 0x25 + static VALUE_RETRY_DEVICE_TYPE = 0x25; // The device RF4CE base channel - static VALUE_RF4CE_BASE_CHANNEL2 = 0x26 + static VALUE_RF4CE_BASE_CHANNEL2 = 0x26; // The RF4CE device types supported by the node - static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST2 = 0x27 + static VALUE_RF4CE_SUPPORTED_DEVICE_TYPES_LIST2 = 0x27; // The RF4CE profiles supported by the node - static VALUE_RF4CE_SUPPORTED_PROFILES_LIST2 = 0x28 + static VALUE_RF4CE_SUPPORTED_PROFILES_LIST2 = 0x28; // Enable or disable packet traffic arbitration. - static VALUE_ENABLE_PTA = 0x31 + static VALUE_ENABLE_PTA = 0x31; // Set packet traffic arbitration configuration options. - static VALUE_PTA_OPTIONS = 0x32 + static VALUE_PTA_OPTIONS = 0x32; // Configure manufacturing library options(0-non-CSMA transmits,1-CSMA transmits). - static VALUE_MFGLIB_OPTIONS = 0x33 + static VALUE_MFGLIB_OPTIONS = 0x33; static VALUE_END_DEVICE_KEEP_ALIVE_SUPPORT_MODE = 0x3F; } @@ -385,16 +385,16 @@ export class EzspExtendedValueId extends basic.uint8_t { // to get the extended value. // The flags field associated with the specified endpoint. - static EXTENDED_VALUE_ENDPOINT_FLAGS = 0x00 + static EXTENDED_VALUE_ENDPOINT_FLAGS = 0x00; // 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 UNKNOWN_NODE_ID (0xFFFD). - static EXTENDED_VALUE_LAST_LEAVE_REASON = 0x01 + static EXTENDED_VALUE_LAST_LEAVE_REASON = 0x01; // This number of bytes of overhead required in the network frame for source // routing to a particular destination. - static EXTENDED_VALUE_GET_SOURCE_ROUTE_OVERHEAD = 0x02 + static EXTENDED_VALUE_GET_SOURCE_ROUTE_OVERHEAD = 0x02; } @@ -402,9 +402,9 @@ export class EzspEndpointFlags extends basic.uint16_t { // Flags associated with the endpoint data configured on the NCP. // Indicates that the endpoint is disabled and NOT discoverable via ZDO. - static ENDPOINT_DISABLED = 0x00 + static ENDPOINT_DISABLED = 0x00; // Indicates that the endpoint is enabled and discoverable via ZDO. - static ENDPOINT_ENABLED = 0x01 + static ENDPOINT_ENABLED = 0x01; } @@ -412,16 +412,16 @@ export class EmberConfigTxPowerMode extends basic.uint16_t { // Values for CONFIG_TX_POWER_MODE. // Normal power mode and bi-directional RF transmitter output. - static TX_POWER_MODE_DEFAULT = 0x00 + static TX_POWER_MODE_DEFAULT = 0x00; // Enable boost power mode. This is a high performance radio mode which // offers increased receive sensitivity and transmit power at the cost of an // increase in power consumption. - static TX_POWER_MODE_BOOST = 0x01 + static TX_POWER_MODE_BOOST = 0x01; // Enable the alternate transmitter output. This allows for simplified // connection to an external power amplifier via the RF_TX_ALT_P and // RF_TX_ALT_N pins. TX_POWER_MODE_BOOST_AND_ALTERNATE 0x03 Enable both // boost mode and the alternate transmitter output. - static TX_POWER_MODE_ALTERNATE = 0x02 + static TX_POWER_MODE_ALTERNATE = 0x02; } @@ -429,29 +429,29 @@ export class EzspPolicyId extends basic.uint8_t { // Identifies a policy. // Controls trust center behavior. - static TRUST_CENTER_POLICY = 0x00 + static TRUST_CENTER_POLICY = 0x00; // Controls how external binding modification requests are handled. - static BINDING_MODIFICATION_POLICY = 0x01 + static BINDING_MODIFICATION_POLICY = 0x01; // Controls whether the Host supplies unicast replies. - static UNICAST_REPLIES_POLICY = 0x02 + static UNICAST_REPLIES_POLICY = 0x02; // Controls whether pollHandler callbacks are generated. - static POLL_HANDLER_POLICY = 0x03 + static POLL_HANDLER_POLICY = 0x03; // Controls whether the message contents are included in the // messageSentHandler callback. - static MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04 + static MESSAGE_CONTENTS_IN_CALLBACK_POLICY = 0x04; // Controls whether the Trust Center will respond to Trust Center link key // requests. - static TC_KEY_REQUEST_POLICY = 0x05 + static TC_KEY_REQUEST_POLICY = 0x05; // Controls whether the Trust Center will respond to application link key // requests. - static APP_KEY_REQUEST_POLICY = 0x06 + static 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. - static PACKET_VALIDATE_LIBRARY_POLICY = 0x07 + static PACKET_VALIDATE_LIBRARY_POLICY = 0x07; // Controls whether the stack will process ZLL messages. - static ZLL_POLICY = 0x08 + static ZLL_POLICY = 0x08; - static TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09 + static TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY = 0x09; } @@ -459,77 +459,77 @@ export class EzspDecisionId extends basic.uint16_t { // Identifies a policy decision. // Send the network key in the clear to all joining and rejoining devices. - static ALLOW_JOINS = 0x00 + static ALLOW_JOINS = 0x00; // Send the network key in the clear to all joining devices. Rejoining // devices are sent the network key encrypted with their trust center link // key. The trust center and any rejoining device are assumed to share a // link key, either preconfigured or obtained under a previous policy. - static ALLOW_JOINS_REJOINS_HAVE_LINK_KEY = 0x04 + static ALLOW_JOINS_REJOINS_HAVE_LINK_KEY = 0x04; // Send the network key encrypted with the joining or rejoining device's // trust center link key. The trust center and any joining or rejoining // device are assumed to share a link key, either preconfigured or obtained // under a previous policy. This is the default value for the // TRUST_CENTER_POLICY. - static ALLOW_PRECONFIGURED_KEY_JOINS = 0x01 + static ALLOW_PRECONFIGURED_KEY_JOINS = 0x01; // Send the network key encrypted with the rejoining device's trust center // link key. The trust center and any rejoining device are assumed to share // a link key, either preconfigured or obtained under a previous policy. No // new devices are allowed to join. - static ALLOW_REJOINS_ONLY = 0x02 + static ALLOW_REJOINS_ONLY = 0x02; // Reject all unsecured join and rejoin attempts. - static DISALLOW_ALL_JOINS_AND_REJOINS = 0x03 + static DISALLOW_ALL_JOINS_AND_REJOINS = 0x03; // Take no action on trust center rejoin attempts. - static IGNORE_TRUST_CENTER_REJOINS = 0x05 + static IGNORE_TRUST_CENTER_REJOINS = 0x05; // BINDING_MODIFICATION_POLICY default decision. Do not allow the local // binding table to be changed by remote nodes. - static DISALLOW_BINDING_MODIFICATION = 0x10 + static DISALLOW_BINDING_MODIFICATION = 0x10; // BINDING_MODIFICATION_POLICY decision. Allow remote nodes to change // the local binding table. - static ALLOW_BINDING_MODIFICATION = 0x11 + static 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. - static CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS = 0x12 + static 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. - static HOST_WILL_NOT_SUPPLY_REPLY = 0x20 + static 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. - static HOST_WILL_SUPPLY_REPLY = 0x21 + static HOST_WILL_SUPPLY_REPLY = 0x21; // POLL_HANDLER_POLICY default decision. Do not inform the Host when a child // polls. - static POLL_HANDLER_IGNORE = 0x30 + static POLL_HANDLER_IGNORE = 0x30; // POLL_HANDLER_POLICY decision. Generate a pollHandler callback when a // child polls. - static POLL_HANDLER_CALLBACK = 0x31 + static POLL_HANDLER_CALLBACK = 0x31; // MESSAGE_CONTENTS_IN_CALLBACK_POLICY default decision. Include only the // message tag in the messageSentHandler callback. - static MESSAGE_TAG_ONLY_IN_CALLBACK = 0x40 + static 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. - static MESSAGE_TAG_AND_CONTENTS_IN_CALLBACK = 0x41 + static 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. - static DENY_TC_KEY_REQUESTS = 0x50 + static 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. - static ALLOW_TC_KEY_REQUESTS = 0x51 + static ALLOW_TC_KEY_REQUESTS = 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. - static GENERATE_NEW_TC_LINK_KEY = 0x52 + static GENERATE_NEW_TC_LINK_KEY = 0x52; // APP_KEY_REQUEST_POLICY decision. When the Trust Center receives a request // for an application link key, it will be ignored. - static DENY_APP_KEY_REQUESTS = 0x60 + static 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. - static ALLOW_APP_KEY_REQUESTS = 0x61 + static ALLOW_APP_KEY_REQUESTS = 0x61; // Indicates that packet validate library checks are enabled on the NCP. - static PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62 + static PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED = 0x62; // Indicates that packet validate library checks are NOT enabled on the NCP. - static PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63 + static PACKET_VALIDATE_LIBRARY_CHECKS_DISABLED = 0x63; } @@ -537,432 +537,432 @@ export class EzspMfgTokenId extends basic.uint8_t { // Manufacturing token IDs used by ezspGetMfgToken(). // Custom version (2 bytes). - static MFG_CUSTOM_VERSION = 0x00 + static MFG_CUSTOM_VERSION = 0x00; // Manufacturing string (16 bytes). - static MFG_STRING = 0x01 + static MFG_STRING = 0x01; // Board name (16 bytes). - static MFG_BOARD_NAME = 0x02 + static MFG_BOARD_NAME = 0x02; // Manufacturing ID (2 bytes). - static MFG_MANUF_ID = 0x03 + static MFG_MANUF_ID = 0x03; // Radio configuration (2 bytes). - static MFG_PHY_CONFIG = 0x04 + static MFG_PHY_CONFIG = 0x04; // Bootload AES key (16 bytes). - static MFG_BOOTLOAD_AES_KEY = 0x05 + static MFG_BOOTLOAD_AES_KEY = 0x05; // ASH configuration (40 bytes). - static MFG_ASH_CONFIG = 0x06 + static MFG_ASH_CONFIG = 0x06; // EZSP storage (8 bytes). - static MFG_STORAGE = 0x07 + static MFG_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. - static STACK_CAL_DATA = 0x08 + static STACK_CAL_DATA = 0x08; // Certificate Based Key Exchange (CBKE) data (92 bytes). - static MFG_CBKE_DATA = 0x09 + static MFG_CBKE_DATA = 0x09; // Installation code (20 bytes). - static MFG_INSTALLATION_CODE = 0x0A + static MFG_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. - static STACK_CAL_FILTER = 0x0B + static STACK_CAL_FILTER = 0x0B; // Custom EUI64 MAC address (8 bytes). - static MFG_CUSTOM_EUI_64 = 0x0C + static MFG_CUSTOM_EUI_64 = 0x0C; // CTUNE value (2 byte). - static MFG_CTUNE = 0x0D + static MFG_CTUNE = 0x0D; } export class EzspStatus extends basic.uint8_t { // Status values used by EZSP. // Success. - static SUCCESS = 0x00 + static SUCCESS = 0x00; // Fatal error. - static SPI_ERR_FATAL = 0x10 + static SPI_ERR_FATAL = 0x10; // The Response frame of the current transaction indicates the NCP has // reset. - static SPI_ERR_NCP_RESET = 0x11 + static 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). - static SPI_ERR_OVERSIZED_FRAME = 0x12 + static SPI_ERR_OVERSIZED_FRAME = 0x12; // The Response frame of the current transaction indicates the previous // transaction was aborted (nSSEL deasserted too soon). - static SPI_ERR_ABORTED_TRANSACTION = 0x13 + static SPI_ERR_ABORTED_TRANSACTION = 0x13; // The Response frame of the current transaction indicates the frame // terminator is missing from the Command frame. - static SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14 + static SPI_ERR_MISSING_FRAME_TERMINATOR = 0x14; // The NCP has not provided a Response within the time limit defined by // WAIT_SECTION_TIMEOUT. - static SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15 + static SPI_ERR_WAIT_SECTION_TIMEOUT = 0x15; // The Response frame from the NCP is missing the frame terminator. - static SPI_ERR_NO_FRAME_TERMINATOR = 0x16 + static 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. - static SPI_ERR_COMMAND_OVERSIZED = 0x17 + static SPI_ERR_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. - static SPI_ERR_RESPONSE_OVERSIZED = 0x18 + static SPI_ERR_RESPONSE_OVERSIZED = 0x18; // The Host has sent the Command and is still waiting for the NCP to send a // Response. - static SPI_WAITING_FOR_RESPONSE = 0x19 + static SPI_WAITING_FOR_RESPONSE = 0x19; // The NCP has not asserted nHOST_INT within the time limit defined by // WAKE_HANDSHAKE_TIMEOUT. - static SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A + static SPI_ERR_HANDSHAKE_TIMEOUT = 0x1A; // The NCP has not asserted nHOST_INT after an NCP reset within the time // limit defined by STARTUP_TIMEOUT. - static SPI_ERR_STARTUP_TIMEOUT = 0x1B + static SPI_ERR_STARTUP_TIMEOUT = 0x1B; // The Host attempted to verify the SPI Protocol activity and version // number, and the verification failed. - static SPI_ERR_STARTUP_FAIL = 0x1C + static 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. - static SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D + static SPI_ERR_UNSUPPORTED_SPI_COMMAND = 0x1D; // Operation not yet complete. - static ASH_IN_PROGRESS = 0x20 + static ASH_IN_PROGRESS = 0x20; // Fatal error detected by host. - static HOST_FATAL_ERROR = 0x21 + static HOST_FATAL_ERROR = 0x21; // Fatal error detected by NCP. - static ASH_NCP_FATAL_ERROR = 0x22 + static ASH_NCP_FATAL_ERROR = 0x22; // Tried to send DATA frame too long. - static DATA_FRAME_TOO_LONG = 0x23 + static DATA_FRAME_TOO_LONG = 0x23; // Tried to send DATA frame too short. - static DATA_FRAME_TOO_SHORT = 0x24 + static DATA_FRAME_TOO_SHORT = 0x24; // No space for tx'ed DATA frame. - static NO_TX_SPACE = 0x25 + static NO_TX_SPACE = 0x25; // No space for rec'd DATA frame. - static NO_RX_SPACE = 0x26 + static NO_RX_SPACE = 0x26; // No receive data available. - static NO_RX_DATA = 0x27 + static NO_RX_DATA = 0x27; // Not in Connected state. - static NOT_CONNECTED = 0x28 + static NOT_CONNECTED = 0x28; // The NCP received a command before the EZSP version had been set. - static ERROR_VERSION_NOT_SET = 0x30 + static ERROR_VERSION_NOT_SET = 0x30; // The NCP received a command containing an unsupported frame ID. - static ERROR_INVALID_FRAME_ID = 0x31 + static ERROR_INVALID_FRAME_ID = 0x31; // The direction flag in the frame control field was incorrect. - static ERROR_WRONG_DIRECTION = 0x32 + static 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. - static ERROR_TRUNCATED = 0x33 + static 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. - static ERROR_OVERFLOW = 0x34 + static ERROR_OVERFLOW = 0x34; // Insufficient memory was available. - static ERROR_OUT_OF_MEMORY = 0x35 + static ERROR_OUT_OF_MEMORY = 0x35; // The value was out of bounds. - static ERROR_INVALID_VALUE = 0x36 + static ERROR_INVALID_VALUE = 0x36; // The configuration id was not recognized. - static ERROR_INVALID_ID = 0x37 + static ERROR_INVALID_ID = 0x37; // Configuration values can no longer be modified. - static ERROR_INVALID_CALL = 0x38 + static ERROR_INVALID_CALL = 0x38; // The NCP failed to respond to a command. - static ERROR_NO_RESPONSE = 0x39 + static ERROR_NO_RESPONSE = 0x39; // The length of the command exceeded the maximum EZSP frame length. - static ERROR_COMMAND_TOO_LONG = 0x40 + static ERROR_COMMAND_TOO_LONG = 0x40; // The UART receive queue was full causing a callback response to be // dropped. - static ERROR_QUEUE_FULL = 0x41 + static ERROR_QUEUE_FULL = 0x41; // The command has been filtered out by NCP. - static ERROR_COMMAND_FILTERED = 0x42 + static ERROR_COMMAND_FILTERED = 0x42; // EZSP Security Key is already set - static ERROR_SECURITY_KEY_ALREADY_SET = 0x43 + static ERROR_SECURITY_KEY_ALREADY_SET = 0x43; // EZSP Security Type is invalid - static ERROR_SECURITY_TYPE_INVALID = 0x44 + static ERROR_SECURITY_TYPE_INVALID = 0x44; // EZSP Security Parameters are invalid - static ERROR_SECURITY_PARAMETERS_INVALID = 0x45 + static ERROR_SECURITY_PARAMETERS_INVALID = 0x45; // EZSP Security Parameters are already set - static ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46 + static ERROR_SECURITY_PARAMETERS_ALREADY_SET = 0x46; // EZSP Security Key is not set - static ERROR_SECURITY_KEY_NOT_SET = 0x47 + static ERROR_SECURITY_KEY_NOT_SET = 0x47; // EZSP Security Parameters are not set - static ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48 + static ERROR_SECURITY_PARAMETERS_NOT_SET = 0x48; // Received frame with unsupported control byte - static ERROR_UNSUPPORTED_CONTROL = 0x49 + static ERROR_UNSUPPORTED_CONTROL = 0x49; // Received frame is unsecure, when security is established - static ERROR_UNSECURE_FRAME = 0x4A + static ERROR_UNSECURE_FRAME = 0x4A; // Incompatible ASH version - static ASH_ERROR_VERSION = 0x50 + static ASH_ERROR_VERSION = 0x50; // Exceeded max ACK timeouts - static ASH_ERROR_TIMEOUTS = 0x51 + static ASH_ERROR_TIMEOUTS = 0x51; // Timed out waiting for RSTACK - static ASH_ERROR_RESET_FAIL = 0x52 + static ASH_ERROR_RESET_FAIL = 0x52; // Unexpected ncp reset - static ASH_ERROR_NCP_RESET = 0x53 + static ASH_ERROR_NCP_RESET = 0x53; // Serial port initialization failed - static ERROR_SERIAL_INIT = 0x54 + static ERROR_SERIAL_INIT = 0x54; // Invalid ncp processor type - static ASH_ERROR_NCP_TYPE = 0x55 + static ASH_ERROR_NCP_TYPE = 0x55; // Invalid ncp reset method - static ASH_ERROR_RESET_METHOD = 0x56 + static ASH_ERROR_RESET_METHOD = 0x56; // XON/XOFF not supported by host driver - static ASH_ERROR_XON_XOFF = 0x57 + static ASH_ERROR_XON_XOFF = 0x57; // ASH protocol started - static ASH_STARTED = 0x70 + static ASH_STARTED = 0x70; // ASH protocol connected - static ASH_CONNECTED = 0x71 + static ASH_CONNECTED = 0x71; // ASH protocol disconnected - static ASH_DISCONNECTED = 0x72 + static ASH_DISCONNECTED = 0x72; // Timer expired waiting for ack - static ASH_ACK_TIMEOUT = 0x73 + static ASH_ACK_TIMEOUT = 0x73; // Frame in progress cancelled - static ASH_CANCELLED = 0x74 + static ASH_CANCELLED = 0x74; // Received frame out of sequence - static ASH_OUT_OF_SEQUENCE = 0x75 + static ASH_OUT_OF_SEQUENCE = 0x75; // Received frame with CRC error - static ASH_BAD_CRC = 0x76 + static ASH_BAD_CRC = 0x76; // Received frame with comm error - static ASH_COMM_ERROR = 0x77 + static ASH_COMM_ERROR = 0x77; // Received frame with bad ackNum - static ASH_BAD_ACKNUM = 0x78 + static ASH_BAD_ACKNUM = 0x78; // Received frame shorter than minimum - static ASH_TOO_SHORT = 0x79 + static ASH_TOO_SHORT = 0x79; // Received frame longer than maximum - static ASH_TOO_LONG = 0x7A + static ASH_TOO_LONG = 0x7A; // Received frame with illegal control byte - static ASH_BAD_CONTROL = 0x7B + static ASH_BAD_CONTROL = 0x7B; // Received frame with illegal length for its type - static ASH_BAD_LENGTH = 0x7C + static ASH_BAD_LENGTH = 0x7C; // Received ASH Ack - static ASH_ACK_RECEIVED = 0x7D + static ASH_ACK_RECEIVED = 0x7D; // Sent ASH Ack - static ASH_ACK_SENT = 0x7E + static ASH_ACK_SENT = 0x7E; // No reset or error - static NO_ERROR = 0xFF + static NO_ERROR = 0xFF; } export class EmberStatus extends basic.uint8_t { // Return type for stack functions. // The generic 'no error' message. - static SUCCESS = 0x00 + static SUCCESS = 0x00; // The generic 'fatal error' message. - static ERR_FATAL = 0x01 + static ERR_FATAL = 0x01; // An invalid value was passed as an argument to a function - static BAD_ARGUMENT = 0x02 + static BAD_ARGUMENT = 0x02; // The manufacturing and stack token format in nonvolatile memory is // different than what the stack expects (returned at initialization). - static EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04 + static EEPROM_MFG_STACK_VERSION_MISMATCH = 0x04; // The static memory definitions in ember-staticmemory.h are incompatible // with this stack version. - static INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS = 0x05 + static INCOMPATIBLE_STATIC_MEMORY_DEFINITIONS = 0x05; // The manufacturing token format in non-volatile memory is different than // what the stack expects (returned at initialization). - static EEPROM_MFG_VERSION_MISMATCH = 0x06 + static EEPROM_MFG_VERSION_MISMATCH = 0x06; // The stack token format in non-volatile memory is different than what the // stack expects (returned at initialization). - static EEPROM_STACK_VERSION_MISMATCH = 0x07 + static EEPROM_STACK_VERSION_MISMATCH = 0x07; // There are no more buffers. - static NO_BUFFERS = 0x18 + static NO_BUFFERS = 0x18; // Specified an invalid baud rate. - static SERIAL_INVALID_BAUD_RATE = 0x20 + static SERIAL_INVALID_BAUD_RATE = 0x20; // Specified an invalid serial port. - static SERIAL_INVALID_PORT = 0x21 + static SERIAL_INVALID_PORT = 0x21; // Tried to send too much data. - static SERIAL_TX_OVERFLOW = 0x22 + static SERIAL_TX_OVERFLOW = 0x22; // There was not enough space to store a received character and the // character was dropped. - static SERIAL_RX_OVERFLOW = 0x23 + static SERIAL_RX_OVERFLOW = 0x23; // Detected a UART framing error. - static SERIAL_RX_FRAME_ERROR = 0x24 + static SERIAL_RX_FRAME_ERROR = 0x24; // Detected a UART parity error. - static SERIAL_RX_PARITY_ERROR = 0x25 + static SERIAL_RX_PARITY_ERROR = 0x25; // There is no received data to process. - static SERIAL_RX_EMPTY = 0x26 + static SERIAL_RX_EMPTY = 0x26; // The receive interrupt was not handled in time, and a character was // dropped. - static SERIAL_RX_OVERRUN_ERROR = 0x27 + static SERIAL_RX_OVERRUN_ERROR = 0x27; // The MAC transmit queue is full. - static MAC_TRANSMIT_QUEUE_FULL = 0x39 + static MAC_TRANSMIT_QUEUE_FULL = 0x39; // MAC header FCR error on receive. - static MAC_UNKNOWN_HEADER_TYPE = 0x3A + static MAC_UNKNOWN_HEADER_TYPE = 0x3A; // The MAC can't complete this task because it is scanning. - static MAC_SCANNING = 0x3D + static MAC_SCANNING = 0x3D; // No pending data exists for device doing a data poll. - static MAC_NO_DATA = 0x31 + static MAC_NO_DATA = 0x31; // Attempt to scan when we are joined to a network. - static MAC_JOINED_NETWORK = 0x32 + static MAC_JOINED_NETWORK = 0x32; // Scan duration must be 0 to 14 inclusive. Attempt was made to scan with an // incorrect duration value. - static MAC_BAD_SCAN_DURATION = 0x33 + static MAC_BAD_SCAN_DURATION = 0x33; // emberStartScan was called with an incorrect scan type. - static MAC_INCORRECT_SCAN_TYPE = 0x34 + static MAC_INCORRECT_SCAN_TYPE = 0x34; // emberStartScan was called with an invalid channel mask. - static MAC_INVALID_CHANNEL_MASK = 0x35 + static MAC_INVALID_CHANNEL_MASK = 0x35; // Failed to scan current channel because we were unable to transmit the // relevant MAC command. - static MAC_COMMAND_TRANSMIT_FAILURE = 0x36 + static MAC_COMMAND_TRANSMIT_FAILURE = 0x36; // We expected to receive an ACK following the transmission, but the MAC // level ACK was never received. - static MAC_NO_ACK_RECEIVED = 0x40 + static MAC_NO_ACK_RECEIVED = 0x40; // Indirect data message timed out before polled. - static MAC_INDIRECT_TIMEOUT = 0x42 + static MAC_INDIRECT_TIMEOUT = 0x42; // The Simulated EEPROM is telling the application that there is 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. - static SIM_EEPROM_ERASE_PAGE_GREEN = 0x43 + static SIM_EEPROM_ERASE_PAGE_GREEN = 0x43; // The Simulated EEPROM is telling the application that there is at least // one flash page to be erased. The RED status means the current page has // filled above the ERASE_CRITICAL_THRESHOLD. Due to the shrinking // availability of write space, there is a danger of data loss. The // application must call the function halSimEepromErasePage as soon as // possible to erase a page. - static SIM_EEPROM_ERASE_PAGE_RED = 0x44 + static SIM_EEPROM_ERASE_PAGE_RED = 0x44; // The Simulated EEPROM has run out of room to write any 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. - static SIM_EEPROM_FULL = 0x45 + static SIM_EEPROM_FULL = 0x45; // 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. - static ERR_FLASH_WRITE_INHIBITED = 0x46 + static ERR_FLASH_WRITE_INHIBITED = 0x46; // A fatal error has occurred while trying to write data to the Flash and // the write verification has failed. The 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. - static ERR_FLASH_VERIFY_FAILED = 0x47 + static ERR_FLASH_VERIFY_FAILED = 0x47; // Attempt 1 to initialize the Simulated EEPROM has failed. This failure // means the information already stored in Flash (or a lack thereof), is // fatally incompatible with the token information compiled into the code // image being run. - static SIM_EEPROM_INIT_1_FAILED = 0x48 + static 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. - static SIM_EEPROM_INIT_2_FAILED = 0x49 + static 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. - static SIM_EEPROM_INIT_3_FAILED = 0x4A + static SIM_EEPROM_INIT_3_FAILED = 0x4A; // A fatal error has occurred while trying to write data to the flash, // possibly due to write protection or an invalid address. The 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. - static ERR_FLASH_PROG_FAIL = 0x4B + static ERR_FLASH_PROG_FAIL = 0x4B; // A fatal error has occurred while trying to erase flash, possibly due to // write protection. The 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. - static ERR_FLASH_ERASE_FAIL = 0x4C + static ERR_FLASH_ERASE_FAIL = 0x4C; // The bootloader received an invalid message (failed attempt to go into // bootloader). - static ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58 + static ERR_BOOTLOADER_TRAP_TABLE_BAD = 0x58; // Bootloader received an invalid message (failed attempt to go into // bootloader). - static ERR_BOOTLOADER_TRAP_UNKNOWN = 0x59 + static 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. - static ERR_BOOTLOADER_NO_IMAGE = 0x5A + static ERR_BOOTLOADER_NO_IMAGE = 0x5A; // The APS layer attempted to send or deliver a message, but it failed. - static DELIVERY_FAILED = 0x66 + static DELIVERY_FAILED = 0x66; // This binding index is out of range of the current binding table. - static BINDING_INDEX_OUT_OF_RANGE = 0x69 + static BINDING_INDEX_OUT_OF_RANGE = 0x69; // This address table index is out of range for the current address table. - static ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A + static ADDRESS_TABLE_INDEX_OUT_OF_RANGE = 0x6A; // An invalid binding table index was given to a function. - static INVALID_BINDING_INDEX = 0x6C + static INVALID_BINDING_INDEX = 0x6C; // The API call is not allowed given the current state of the stack. - static INVALID_CALL = 0x70 + static INVALID_CALL = 0x70; // The link cost to a node is not known. - static COST_NOT_KNOWN = 0x71 + static COST_NOT_KNOWN = 0x71; // The maximum number of in-flight messages (i.e. // APS_UNICAST_MESSAGE_COUNT) has been reached. - static MAX_MESSAGE_LIMIT_REACHED = 0x72 + static MAX_MESSAGE_LIMIT_REACHED = 0x72; // The message to be transmitted is too big to fit into a single over-the- // air packet. - static MESSAGE_TOO_LONG = 0x74 + static MESSAGE_TOO_LONG = 0x74; // The application is trying to delete or overwrite a binding that is in // use. - static BINDING_IS_ACTIVE = 0x75 + static BINDING_IS_ACTIVE = 0x75; // The application is trying to overwrite an address table entry that is in // use. - static ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76 + static ADDRESS_TABLE_ENTRY_IS_ACTIVE = 0x76; // Conversion is complete. - static ADC_CONVERSION_DONE = 0x80 + static ADC_CONVERSION_DONE = 0x80; // Conversion cannot be done because a request is being processed. - static ADC_CONVERSION_BUSY = 0x81 + static ADC_CONVERSION_BUSY = 0x81; // Conversion is deferred until the current request has been processed. - static ADC_CONVERSION_DEFERRED = 0x82 + static ADC_CONVERSION_DEFERRED = 0x82; // No results are pending. - static ADC_NO_CONVERSION_PENDING = 0x84 + static ADC_NO_CONVERSION_PENDING = 0x84; // Sleeping (for a duration) has been abnormally interrupted and exited // prematurely. - static SLEEP_INTERRUPTED = 0x85 + static SLEEP_INTERRUPTED = 0x85; // The transmit hardware buffer underflowed. - static PHY_TX_UNDERFLOW = 0x88 + static PHY_TX_UNDERFLOW = 0x88; // The transmit hardware did not finish transmitting a packet. - static PHY_TX_INCOMPLETE = 0x89 + static PHY_TX_INCOMPLETE = 0x89; // An unsupported channel setting was specified. - static PHY_INVALID_CHANNEL = 0x8A + static PHY_INVALID_CHANNEL = 0x8A; // An unsupported power setting was specified. - static PHY_INVALID_POWER = 0x8B + static PHY_INVALID_POWER = 0x8B; // The packet cannot be transmitted because the physical MAC layer is // currently transmitting a packet. (This is used for the MAC backoff // algorithm.) PHY_TX_CCA_FAIL 0x8D The transmit attempt failed because all // CCA attempts indicated that the channel was busy - static PHY_TX_BUSY = 0x8C + static PHY_TX_BUSY = 0x8C; // The software installed on the hardware doesn't recognize the hardware // radio type. - static PHY_OSCILLATOR_CHECK_FAILED = 0x8E + static PHY_OSCILLATOR_CHECK_FAILED = 0x8E; // The expected ACK was received after the last transmission. - static PHY_ACK_RECEIVED = 0x8F + static PHY_ACK_RECEIVED = 0x8F; // The stack software has completed initialization and is ready to send and // receive packets over the air. - static NETWORK_UP = 0x90 + static NETWORK_UP = 0x90; // The network is not operating. - static NETWORK_DOWN = 0x91 + static NETWORK_DOWN = 0x91; // An attempt to join a network failed. - static JOIN_FAILED = 0x94 + static JOIN_FAILED = 0x94; // After moving, a mobile node's attempt to re-establish contact with the // network failed. - static MOVE_FAILED = 0x96 + static 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. - static CANNOT_JOIN_AS_ROUTER = 0x98 + static CANNOT_JOIN_AS_ROUTER = 0x98; // The local node ID has changed. The application can obtain the new node ID // by calling emberGetNodeId(). - static NODE_ID_CHANGED = 0x99 + static NODE_ID_CHANGED = 0x99; // The local PAN ID has changed. The application can obtain the new PAN ID // by calling emberGetPanId(). - static PAN_ID_CHANGED = 0x9A + static PAN_ID_CHANGED = 0x9A; // An attempt to join or rejoin the network failed because no router beacons // could be heard by the joining node. - static NO_BEACONS = 0xAB + static 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. - static RECEIVED_KEY_IN_THE_CLEAR = 0xAC + static RECEIVED_KEY_IN_THE_CLEAR = 0xAC; // An attempt was made to join a Secured Network, but the device did not // receive a Network Key. - static NO_NETWORK_KEY_RECEIVED = 0xAD + static NO_NETWORK_KEY_RECEIVED = 0xAD; // After a device joined a Secured Network, a Link Key was requested but no // response was ever received. - static NO_LINK_KEY_RECEIVED = 0xAE + static 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. - static PRECONFIGURED_KEY_REQUIRED = 0xAF + static PRECONFIGURED_KEY_REQUIRED = 0xAF; // The node has not joined a network. - static NOT_JOINED = 0x93 + static NOT_JOINED = 0x93; // The chosen security level (the value of SECURITY_LEVEL) is not supported // by the stack. - static INVALID_SECURITY_LEVEL = 0x95 + static INVALID_SECURITY_LEVEL = 0x95; // A message cannot be sent because the network is currently overloaded. - static NETWORK_BUSY = 0xA1 + static NETWORK_BUSY = 0xA1; // The application tried to send a message using an endpoint that it has not // defined. - static INVALID_ENDPOINT = 0xA3 + static 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. - static BINDING_HAS_CHANGED = 0xA4 + static BINDING_HAS_CHANGED = 0xA4; // An attempt to generate random bytes failed because of insufficient random // data from the radio. - static INSUFFICIENT_RANDOM_DATA = 0xA5 + static INSUFFICIENT_RANDOM_DATA = 0xA5; // There was an error in trying to encrypt at the APS Level. This could // result from either an inability to determine the long address of the // recipient from the short address (no entry in the binding table) or there @@ -971,105 +971,105 @@ export class EmberStatus extends basic.uint8_t { // TRUST_CENTER_MASTER_KEY_NOT_SET 0xA7 There was an attempt to form a // network using commercial security without setting the Trust Center master // key first. - static APS_ENCRYPTION_ERROR = 0xA6 + static APS_ENCRYPTION_ERROR = 0xA6; // There was an attempt to form or join a network with security without // calling emberSetInitialSecurityState() first. - static SECURITY_STATE_NOT_SET = 0xA8 + static SECURITY_STATE_NOT_SET = 0xA8; // There was an attempt to set an entry in the key table using an invalid // long address. An entry cannot be set using either the local device's or // Trust Center's IEEE address. Or an entry already exists in the table with // the same IEEE address. An Address of all zeros or all F's are not valid // addresses in 802.15.4. - static KEY_TABLE_INVALID_ADDRESS = 0xB3 + static KEY_TABLE_INVALID_ADDRESS = 0xB3; // There was an attempt to set a security configuration that is not valid // given the other security settings. - static SECURITY_CONFIGURATION_INVALID = 0xB7 + static 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. - static TOO_SOON_FOR_SWITCH_KEY = 0xB8 + static TOO_SOON_FOR_SWITCH_KEY = 0xB8; // 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). - static KEY_NOT_AUTHORIZED = 0xBB + static KEY_NOT_AUTHORIZED = 0xBB; // The security data provided was not valid, or an integrity check failed. - static SECURITY_DATA_INVALID = 0xBD + static SECURITY_DATA_INVALID = 0xBD; // A ZigBee route error command frame was received indicating that a source // routed message from this node failed en route. - static SOURCE_ROUTE_FAILURE = 0xA9 + static 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. - static MANY_TO_ONE_ROUTE_FAILURE = 0xAA + static MANY_TO_ONE_ROUTE_FAILURE = 0xAA; // A critical and fatal error indicating that the version of the stack // trying to run does not match with the chip it is running on. The software // (stack) on the chip must be replaced with software that is compatible // with the chip. - static STACK_AND_HARDWARE_MISMATCH = 0xB0 + static STACK_AND_HARDWARE_MISMATCH = 0xB0; // An index was passed into the function that was larger than the valid // range. - static INDEX_OUT_OF_RANGE = 0xB1 + static INDEX_OUT_OF_RANGE = 0xB1; // There are no empty entries left in the table. - static TABLE_FULL = 0xB4 + static TABLE_FULL = 0xB4; // The requested table entry has been erased and contains no valid data. - static TABLE_ENTRY_ERASED = 0xB6 + static TABLE_ENTRY_ERASED = 0xB6; // The requested function cannot be executed because the library that // contains the necessary functionality is not present. - static LIBRARY_NOT_PRESENT = 0xB5 + static LIBRARY_NOT_PRESENT = 0xB5; // The stack accepted the command and is currently processing the request. // The results will be returned via an appropriate handler. - static OPERATION_IN_PROGRESS = 0xBA + static OPERATION_IN_PROGRESS = 0xBA; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_0 = 0xF0 + static APPLICATION_ERROR_0 = 0xF0; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_1 = 0xF1 + static APPLICATION_ERROR_1 = 0xF1; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_2 = 0xF2 + static APPLICATION_ERROR_2 = 0xF2; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_3 = 0xF3 + static APPLICATION_ERROR_3 = 0xF3; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_4 = 0xF4 + static APPLICATION_ERROR_4 = 0xF4; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_5 = 0xF5 + static APPLICATION_ERROR_5 = 0xF5; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_6 = 0xF6 + static APPLICATION_ERROR_6 = 0xF6; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_7 = 0xF7 + static APPLICATION_ERROR_7 = 0xF7; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_8 = 0xF8 + static APPLICATION_ERROR_8 = 0xF8; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_9 = 0xF9 + static APPLICATION_ERROR_9 = 0xF9; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_10 = 0xFA + static APPLICATION_ERROR_10 = 0xFA; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_11 = 0xFB + static APPLICATION_ERROR_11 = 0xFB; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_12 = 0xFC + static APPLICATION_ERROR_12 = 0xFC; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_13 = 0xFD + static APPLICATION_ERROR_13 = 0xFD; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_14 = 0xFE + static APPLICATION_ERROR_14 = 0xFE; // This error is reserved for customer application use. This will never be // returned from any portion of the network stack or HAL. - static APPLICATION_ERROR_15 = 0xFF + static APPLICATION_ERROR_15 = 0xFF; } @@ -1078,15 +1078,15 @@ export class EmberEventUnits extends basic.uint8_t { // execution time. // The event is not scheduled to run. - static EVENT_INACTIVE = 0x00 + static EVENT_INACTIVE = 0x00; // The execution time is in approximate milliseconds. - static EVENT_MS_TIME = 0x01 + static EVENT_MS_TIME = 0x01; // The execution time is in 'binary' quarter seconds (256 approximate // milliseconds each). - static EVENT_QS_TIME = 0x02 + static EVENT_QS_TIME = 0x02; // The execution time is in 'binary' minutes (65536 approximate milliseconds // each). - static EVENT_MINUTE_TIME = 0x03 + static EVENT_MINUTE_TIME = 0x03; } @@ -1094,18 +1094,18 @@ export class EmberNodeType extends basic.uint8_t { // The type of the node. // Device is not joined. - static UNKNOWN_DEVICE = 0x00 + static UNKNOWN_DEVICE = 0x00; // Will relay messages and can act as a parent to other nodes. - static COORDINATOR = 0x01 + static COORDINATOR = 0x01; // Will relay messages and can act as a parent to other nodes. - static ROUTER = 0x02 + static ROUTER = 0x02; // Communicates only with its parent and will not relay messages. - static END_DEVICE = 0x03 + static END_DEVICE = 0x03; // An end device whose radio can be turned off to save power. The // application must poll to receive messages. - static SLEEPY_END_DEVICE = 0x04 + static SLEEPY_END_DEVICE = 0x04; // A sleepy end device that can move through the network. - static MOBILE_END_DEVICE = 0x05 + static MOBILE_END_DEVICE = 0x05; } @@ -1113,16 +1113,16 @@ export class EmberNetworkStatus extends basic.uint8_t { // The possible join states for a node. // The node is not associated with a network in any way. - static NO_NETWORK = 0x00 + static NO_NETWORK = 0x00; // The node is currently attempting to join a network. - static JOINING_NETWORK = 0x01 + static JOINING_NETWORK = 0x01; // The node is joined to a network. - static JOINED_NETWORK = 0x02 + static JOINED_NETWORK = 0x02; // The node is an end device joined to a network but its parent is not // responding. - static JOINED_NETWORK_NO_PARENT = 0x03 + static JOINED_NETWORK_NO_PARENT = 0x03; // The node is in the process of leaving its current network. - static LEAVING_NETWORK = 0x04 + static LEAVING_NETWORK = 0x04; } @@ -1130,19 +1130,19 @@ export class EmberIncomingMessageType extends basic.uint8_t { // Incoming message types. // Unicast. - static INCOMING_UNICAST = 0x00 + static INCOMING_UNICAST = 0x00; // Unicast reply. - static INCOMING_UNICAST_REPLY = 0x01 + static INCOMING_UNICAST_REPLY = 0x01; // Multicast. - static INCOMING_MULTICAST = 0x02 + static INCOMING_MULTICAST = 0x02; // Multicast sent by the local device. - static INCOMING_MULTICAST_LOOPBACK = 0x03 + static INCOMING_MULTICAST_LOOPBACK = 0x03; // Broadcast. - static INCOMING_BROADCAST = 0x04 + static INCOMING_BROADCAST = 0x04; // Broadcast sent by the local device. - static INCOMING_BROADCAST_LOOPBACK = 0x05 + static INCOMING_BROADCAST_LOOPBACK = 0x05; // Many to one route request. - static INCOMING_MANY_TO_ONE_ROUTE_REQUEST = 0x06 + static INCOMING_MANY_TO_ONE_ROUTE_REQUEST = 0x06; } @@ -1150,17 +1150,17 @@ export class EmberOutgoingMessageType extends basic.uint8_t { // Outgoing message types. // Unicast sent directly to an EmberNodeId. - static OUTGOING_DIRECT = 0x00 + static OUTGOING_DIRECT = 0x00; // Unicast sent using an entry in the address table. - static OUTGOING_VIA_ADDRESS_TABLE = 0x01 + static OUTGOING_VIA_ADDRESS_TABLE = 0x01; // Unicast sent using an entry in the binding table. - static OUTGOING_VIA_BINDING = 0x02 + static OUTGOING_VIA_BINDING = 0x02; // Multicast message. This value is passed to emberMessageSentHandler() // only. It may not be passed to emberSendUnicast(). - static OUTGOING_MULTICAST = 0x03 + static OUTGOING_MULTICAST = 0x03; // Broadcast message. This value is passed to emberMessageSentHandler() // only. It may not be passed to emberSendUnicast(). - static OUTGOING_BROADCAST = 0x04 + static OUTGOING_BROADCAST = 0x04; } @@ -1168,16 +1168,16 @@ export class EmberMacPassthroughType extends basic.uint8_t { // MAC passthrough message type flags. // No MAC passthrough messages. - static MAC_PASSTHROUGH_NONE = 0x00 + static MAC_PASSTHROUGH_NONE = 0x00; // SE InterPAN messages. - static MAC_PASSTHROUGH_SE_INTERPAN = 0x01 + static MAC_PASSTHROUGH_SE_INTERPAN = 0x01; // Legacy EmberNet messages. - static MAC_PASSTHROUGH_EMBERNET = 0x02 + static MAC_PASSTHROUGH_EMBERNET = 0x02; // Legacy EmberNet messages filtered by their source address. - static MAC_PASSTHROUGH_EMBERNET_SOURCE = 0x04 - static MAC_PASSTHROUGH_APPLICATION = 0x08 - static MAC_PASSTHROUGH_CUSTOM = 0x10 - static MAC_PASSTHROUGH_INTERNAL = 0x80 + static MAC_PASSTHROUGH_EMBERNET_SOURCE = 0x04; + static MAC_PASSTHROUGH_APPLICATION = 0x08; + static MAC_PASSTHROUGH_CUSTOM = 0x10; + static MAC_PASSTHROUGH_INTERNAL = 0x80; } @@ -1185,15 +1185,15 @@ export class EmberBindingType extends basic.uint8_t { // Binding types. // A binding that is currently not in use. - static UNUSED_BINDING = 0x00 + static UNUSED_BINDING = 0x00; // A unicast binding whose 64-bit identifier is the destination EUI64. - static UNICAST_BINDING = 0x01 + static UNICAST_BINDING = 0x01; // A unicast binding whose 64-bit identifier is the aggregator EUI64. - static MANY_TO_ONE_BINDING = 0x02 + static MANY_TO_ONE_BINDING = 0x02; // A multicast binding whose 64-bit identifier is the group address. A // multicast binding can be used to send messages to the group and to // receive messages sent to the group. - static MULTICAST_BINDING = 0x03 + static MULTICAST_BINDING = 0x03; } @@ -1201,39 +1201,39 @@ export class EmberApsOption extends basic.uint16_t { // Options to use when sending a message. // No options. - static APS_OPTION_NONE = 0x0000 + static APS_OPTION_NONE = 0x0000; // UNKNOWN: Discovered while receiving data - static APS_OPTION_UNKNOWN = 0x0008 + static APS_OPTION_UNKNOWN = 0x0008; // Send the message using APS Encryption, using the Link Key shared with the // destination node to encrypt the data at the APS Level. - static APS_OPTION_ENCRYPTION = 0x0020 + static APS_OPTION_ENCRYPTION = 0x0020; // Resend the message using the APS retry mechanism. - static APS_OPTION_RETRY = 0x0040 + static APS_OPTION_RETRY = 0x0040; // Causes a route discovery to be initiated if no route to the destination // is known. - static APS_OPTION_ENABLE_ROUTE_DISCOVERY = 0x0100 + static APS_OPTION_ENABLE_ROUTE_DISCOVERY = 0x0100; // Causes a route discovery to be initiated even if one is known. - static APS_OPTION_FORCE_ROUTE_DISCOVERY = 0x0200 + static APS_OPTION_FORCE_ROUTE_DISCOVERY = 0x0200; // Include the source EUI64 in the network frame. - static APS_OPTION_SOURCE_EUI64 = 0x0400 + static APS_OPTION_SOURCE_EUI64 = 0x0400; // Include the destination EUI64 in the network frame. - static APS_OPTION_DESTINATION_EUI64 = 0x0800 + static APS_OPTION_DESTINATION_EUI64 = 0x0800; // Send a ZDO request to discover the node ID of the destination, if it is // not already know. - static APS_OPTION_ENABLE_ADDRESS_DISCOVERY = 0x1000 + static APS_OPTION_ENABLE_ADDRESS_DISCOVERY = 0x1000; // Reserved. - static APS_OPTION_POLL_RESPONSE = 0x2000 + static APS_OPTION_POLL_RESPONSE = 0x2000; // This incoming message is a ZDO request not handled by the EmberZNet // stack, and the application is responsible for sending a ZDO response. // This flag is used only when the ZDO is configured to have requests // handled by the application. See the CONFIG_APPLICATION_ZDO_FLAGS // configuration parameter for more information. - static APS_OPTION_ZDO_RESPONSE_REQUIRED = 0x4000 + static APS_OPTION_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. - static APS_OPTION_FRAGMENT = 0x8000 + static APS_OPTION_FRAGMENT = 0x8000; } @@ -1241,9 +1241,9 @@ export class EzspNetworkScanType extends basic.uint8_t { // Network scan types. // An energy scan scans each channel for its RSSI value. - static ENERGY_SCAN = 0x00 + static ENERGY_SCAN = 0x00; // An active scan scans each channel for available networks. - static ACTIVE_SCAN = 0x01 + static ACTIVE_SCAN = 0x01; } @@ -1252,15 +1252,15 @@ export class EmberJoinDecision extends basic.uint8_t { // Allow the node to join. The joining node should have a pre-configured // key. The security data sent to it will be encrypted with that key. - static USE_PRECONFIGURED_KEY = 0x00 + static USE_PRECONFIGURED_KEY = 0x00; // Allow the node to join. Send the necessary key (the Network Key in // Standard Security mode, the Trust Center Master in High Security mode) // in-the-clear to the joining device. - static SEND_KEY_IN_THE_CLEAR = 0x01 + static SEND_KEY_IN_THE_CLEAR = 0x01; // Deny join. - static DENY_JOIN = 0x02 + static DENY_JOIN = 0x02; // Take no action. - static NO_ACTION = 0x03 + static NO_ACTION = 0x03; } @@ -1269,50 +1269,50 @@ export class EmberInitialSecurityBitmask extends basic.uint16_t { // security features. // This enables ZigBee Standard Security on the node. - static STANDARD_SECURITY_MODE = 0x0000 + static STANDARD_SECURITY_MODE = 0x0000; // This enables Distributed Trust Center Mode for the device forming the // network. (Previously known as NO_TRUST_CENTER_MODE) - static DISTRIBUTED_TRUST_CENTER_MODE = 0x0002 + static DISTRIBUTED_TRUST_CENTER_MODE = 0x0002; // This enables a Global Link Key for the Trust Center. All nodes will share // the same Trust Center Link Key. - static TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004 + static TRUST_CENTER_GLOBAL_LINK_KEY = 0x0004; // This enables devices that perform MAC Association with a pre-configured // Network Key to join the network. It is only set on the Trust Center. - static PRECONFIGURED_NETWORK_KEY_MODE = 0x0008 + static PRECONFIGURED_NETWORK_KEY_MODE = 0x0008; // This denotes that the preconfiguredKey is not the actual Link Key but a // Secret Key known only to the Trust Center. It is hashed with the IEEE // Address of the destination device in order 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. - static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084 + static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084; // This denotes that the preconfiguredKey element has valid data that should // be used to configure the initial security state. - static HAVE_PRECONFIGURED_KEY = 0x0100 + static HAVE_PRECONFIGURED_KEY = 0x0100; // This denotes that the networkKey element has valid data that should be // used to configure the initial security state. - static HAVE_NETWORK_KEY = 0x0200 + static 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 only necessary if the device does // not have a pre-configured key. - static GET_LINK_KEY_WHEN_JOINING = 0x0400 + static 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 preconfigured 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 utilizing a pre-configured key. - static REQUIRE_ENCRYPTED_KEY = 0x0800 + static 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 in cases where a device is joining to the same // network a again (but not using ::emberRejoinNetwork()) it should keep the // NWK and APS frame counters stored in its tokens. - static NO_FRAME_COUNTER_RESET = 0x1000 + static NO_FRAME_COUNTER_RESET = 0x1000; // This denotes that the device should obtain its preconfigured key from an // installation code stored in the manufacturing token. The token contains a // value that will be hashed to obtain the actual preconfigured key. If that // token is not valid, then the call to emberSetInitialSecurityState() will // fail. - static GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000 + static GET_PRECONFIGURED_KEY_FROM_INSTALL_CODE = 0x2000; // This denotes that the // ::EmberInitialSecurityState::preconfiguredTrustCenterEui64 has a value in // it containing the trust center EUI64. The device will only join a network @@ -1323,7 +1323,7 @@ export class EmberInitialSecurityBitmask extends basic.uint16_t { // this bit must be set and the field // ::EmberInitialSecurityState::preconfiguredTrustCenterEui64 must be // populated with the appropriate EUI64. - static HAVE_TRUST_CENTER_EUI64 = 0x0040 + static HAVE_TRUST_CENTER_EUI64 = 0x0040; } @@ -1333,20 +1333,20 @@ export class EmberCurrentSecurityBitmask extends basic.uint16_t { // This denotes that the device is running in a network with ZigBee Standard // Security. - static STANDARD_SECURITY_MODE = 0x0000 + static STANDARD_SECURITY_MODE = 0x0000; // This denotes that the device is running in a network with ZigBee High // Security. - static HIGH_SECURITY_MODE = 0x0001 + static HIGH_SECURITY_MODE = 0x0001; // This denotes that the device is running in a network without a // centralized Trust Center. - static DISTRIBUTED_TRUST_CENTER_MODE = 0x0002 + static 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. - static GLOBAL_LINK_KEY = 0x0004 + static GLOBAL_LINK_KEY = 0x0004; // This denotes that the node has a Trust Center Link Key. - static HAVE_TRUST_CENTER_LINK_KEY = 0x0010 + static HAVE_TRUST_CENTER_LINK_KEY = 0x0010; // This denotes that the Trust Center is using a Hashed Link Key. - static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084 + static TRUST_CENTER_USES_HASHED_LINK_KEY = 0x0084; } @@ -1354,69 +1354,69 @@ export class EmberKeyType extends basic.uint8_t { // Describes the type of ZigBee security key. // A shared key between the Trust Center and a device. - static TRUST_CENTER_LINK_KEY = 0x01 + static TRUST_CENTER_LINK_KEY = 0x01; // A shared secret used for deriving keys between the Trust Center and a // device - static TRUST_CENTER_MASTER_KEY = 0x02 + static TRUST_CENTER_MASTER_KEY = 0x02; // The current active Network Key used by all devices in the network. - static CURRENT_NETWORK_KEY = 0x03 + static CURRENT_NETWORK_KEY = 0x03; // The alternate Network Key that was previously in use, or the newer key // that will be switched to. - static NEXT_NETWORK_KEY = 0x04 + static NEXT_NETWORK_KEY = 0x04; // An Application Link Key shared with another (non-Trust Center) device. - static APPLICATION_LINK_KEY = 0x05 + static APPLICATION_LINK_KEY = 0x05; // An Application Master Key shared secret used to derive an Application // Link Key. - static APPLICATION_MASTER_KEY = 0x06 + static APPLICATION_MASTER_KEY = 0x06; } export class EmberKeyStructBitmask extends basic.uint16_t { // Describes the presence of valid data within the EmberKeyStruct structure. // The key has a sequence number associated with it. - static KEY_HAS_SEQUENCE_NUMBER = 0x0001 + static KEY_HAS_SEQUENCE_NUMBER = 0x0001; // The key has an outgoing frame counter associated with it. - static KEY_HAS_OUTGOING_FRAME_COUNTER = 0x0002 + static KEY_HAS_OUTGOING_FRAME_COUNTER = 0x0002; // The key has an incoming frame counter associated with it. - static KEY_HAS_INCOMING_FRAME_COUNTER = 0x0004 + static KEY_HAS_INCOMING_FRAME_COUNTER = 0x0004; // The key has a Partner IEEE address associated with it. - static KEY_HAS_PARTNER_EUI64 = 0x0008 + static KEY_HAS_PARTNER_EUI64 = 0x0008; } export class EmberDeviceUpdate extends basic.uint8_t { // The status of the device update. - static STANDARD_SECURITY_SECURED_REJOIN = 0x0 - static STANDARD_SECURITY_UNSECURED_JOIN = 0x1 - static DEVICE_LEFT = 0x2 - static STANDARD_SECURITY_UNSECURED_REJOIN = 0x3 - static HIGH_SECURITY_SECURED_REJOIN = 0x4 - static HIGH_SECURITY_UNSECURED_JOIN = 0x5 - static HIGH_SECURITY_UNSECURED_REJOIN = 0x7 + static STANDARD_SECURITY_SECURED_REJOIN = 0x0; + static STANDARD_SECURITY_UNSECURED_JOIN = 0x1; + static DEVICE_LEFT = 0x2; + static STANDARD_SECURITY_UNSECURED_REJOIN = 0x3; + static HIGH_SECURITY_SECURED_REJOIN = 0x4; + static HIGH_SECURITY_UNSECURED_JOIN = 0x5; + static HIGH_SECURITY_UNSECURED_REJOIN = 0x7; } export class EmberKeyStatus extends basic.uint8_t { // The status of the attempt to establish a key. - static APP_LINK_KEY_ESTABLISHED = 0x01 - static APP_MASTER_KEY_ESTABLISHED = 0x02 - static TRUST_CENTER_LINK_KEY_ESTABLISHED = 0x03 - static KEY_ESTABLISHMENT_TIMEOUT = 0x04 - static KEY_TABLE_FULL = 0x05 - static TC_RESPONDED_TO_KEY_REQUEST = 0x06 - static TC_APP_KEY_SENT_TO_REQUESTER = 0x07 - static TC_RESPONSE_TO_KEY_REQUEST_FAILED = 0x08 - static TC_REQUEST_KEY_TYPE_NOT_SUPPORTED = 0x09 - static TC_NO_LINK_KEY_FOR_REQUESTER = 0x0A - static TC_REQUESTER_EUI64_UNKNOWN = 0x0B - static TC_RECEIVED_FIRST_APP_KEY_REQUEST = 0x0C - static TC_TIMEOUT_WAITING_FOR_SECOND_APP_KEY_REQUEST = 0x0D - static TC_NON_MATCHING_APP_KEY_REQUEST_RECEIVED = 0x0E - static TC_FAILED_TO_SEND_APP_KEYS = 0x0F - static TC_FAILED_TO_STORE_APP_KEY_REQUEST = 0x10 - static TC_REJECTED_APP_KEY_REQUEST = 0x11 + static APP_LINK_KEY_ESTABLISHED = 0x01; + static APP_MASTER_KEY_ESTABLISHED = 0x02; + static TRUST_CENTER_LINK_KEY_ESTABLISHED = 0x03; + static KEY_ESTABLISHMENT_TIMEOUT = 0x04; + static KEY_TABLE_FULL = 0x05; + static TC_RESPONDED_TO_KEY_REQUEST = 0x06; + static TC_APP_KEY_SENT_TO_REQUESTER = 0x07; + static TC_RESPONSE_TO_KEY_REQUEST_FAILED = 0x08; + static TC_REQUEST_KEY_TYPE_NOT_SUPPORTED = 0x09; + static TC_NO_LINK_KEY_FOR_REQUESTER = 0x0A; + static TC_REQUESTER_EUI64_UNKNOWN = 0x0B; + static TC_RECEIVED_FIRST_APP_KEY_REQUEST = 0x0C; + static TC_TIMEOUT_WAITING_FOR_SECOND_APP_KEY_REQUEST = 0x0D; + static TC_NON_MATCHING_APP_KEY_REQUEST_RECEIVED = 0x0E; + static TC_FAILED_TO_SEND_APP_KEYS = 0x0F; + static TC_FAILED_TO_STORE_APP_KEY_REQUEST = 0x10; + static TC_REJECTED_APP_KEY_REQUEST = 0x11; } export class EmberCounterType extends basic.uint8_t { @@ -1424,102 +1424,102 @@ export class EmberCounterType extends basic.uint8_t { // readAndClearCounters command. // The MAC received a broadcast. - static COUNTER_MAC_RX_BROADCAST = 0 + static COUNTER_MAC_RX_BROADCAST = 0; // The MAC transmitted a broadcast. - static COUNTER_MAC_TX_BROADCAST = 1 + static COUNTER_MAC_TX_BROADCAST = 1; // The MAC received a unicast. - static COUNTER_MAC_RX_UNICAST = 2 + static COUNTER_MAC_RX_UNICAST = 2; // The MAC successfully transmitted a unicast. - static COUNTER_MAC_TX_UNICAST_SUCCESS = 3 + static COUNTER_MAC_TX_UNICAST_SUCCESS = 3; // The MAC retried a unicast. - static COUNTER_MAC_TX_UNICAST_RETRY = 4 + static COUNTER_MAC_TX_UNICAST_RETRY = 4; // The MAC unsuccessfully transmitted a unicast. - static COUNTER_MAC_TX_UNICAST_FAILED = 5 + static COUNTER_MAC_TX_UNICAST_FAILED = 5; // The APS layer received a data broadcast. - static COUNTER_APS_DATA_RX_BROADCAST = 6 + static COUNTER_APS_DATA_RX_BROADCAST = 6; // The APS layer transmitted a data broadcast. - static COUNTER_APS_DATA_TX_BROADCAST = 7 + static COUNTER_APS_DATA_TX_BROADCAST = 7; // The APS layer received a data unicast. - static COUNTER_APS_DATA_RX_UNICAST = 8 + static COUNTER_APS_DATA_RX_UNICAST = 8; // The APS layer successfully transmitted a data unicast. - static COUNTER_APS_DATA_TX_UNICAST_SUCCESS = 9 + static COUNTER_APS_DATA_TX_UNICAST_SUCCESS = 9; // The APS layer retried a data unicast. - static COUNTER_APS_DATA_TX_UNICAST_RETRY = 10 + static COUNTER_APS_DATA_TX_UNICAST_RETRY = 10; // The APS layer unsuccessfully transmitted a data unicast. - static COUNTER_APS_DATA_TX_UNICAST_FAILED = 11 + static COUNTER_APS_DATA_TX_UNICAST_FAILED = 11; // The network layer successfully submitted a new route discovery to the // MAC. - static COUNTER_ROUTE_DISCOVERY_INITIATED = 12 + static COUNTER_ROUTE_DISCOVERY_INITIATED = 12; // An entry was added to the neighbor table. - static COUNTER_NEIGHBOR_ADDED = 13 + static COUNTER_NEIGHBOR_ADDED = 13; // An entry was removed from the neighbor table. - static COUNTER_NEIGHBOR_REMOVED = 14 + static COUNTER_NEIGHBOR_REMOVED = 14; // A neighbor table entry became stale because it had not been heard from. - static COUNTER_NEIGHBOR_STALE = 15 + static COUNTER_NEIGHBOR_STALE = 15; // A node joined or rejoined to the network via this node. - static COUNTER_JOIN_INDICATION = 16 + static COUNTER_JOIN_INDICATION = 16; // An entry was removed from the child table. - static COUNTER_CHILD_REMOVED = 17 + static COUNTER_CHILD_REMOVED = 17; // EZSP-UART only. An overflow error occurred in the UART. - static COUNTER_ASH_OVERFLOW_ERROR = 18 + static COUNTER_ASH_OVERFLOW_ERROR = 18; // EZSP-UART only. A framing error occurred in the UART. - static COUNTER_ASH_FRAMING_ERROR = 19 + static COUNTER_ASH_FRAMING_ERROR = 19; // EZSP-UART only. An overrun error occurred in the UART. - static COUNTER_ASH_OVERRUN_ERROR = 20 + static COUNTER_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. - static COUNTER_NWK_FRAME_COUNTER_FAILURE = 21 + static COUNTER_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. - static COUNTER_APS_FRAME_COUNTER_FAILURE = 22 + static COUNTER_APS_FRAME_COUNTER_FAILURE = 22; // Utility counter for general debugging use. - static COUNTER_UTILITY = 23 + static COUNTER_UTILITY = 23; // A message was dropped at the APS layer because it had APS encryption but // the key associated with the sender has not been authenticated, and thus // the key is not authorized for use in APS data messages. - static COUNTER_APS_LINK_KEY_NOT_AUTHORIZED = 24 + static COUNTER_APS_LINK_KEY_NOT_AUTHORIZED = 24; // A NWK encrypted message was received but dropped because decryption // failed. - static COUNTER_NWK_DECRYPTION_FAILURE = 25 + static COUNTER_NWK_DECRYPTION_FAILURE = 25; // An APS encrypted message was received but dropped because decryption // failed. - static COUNTER_APS_DECRYPTION_FAILURE = 26 + static COUNTER_APS_DECRYPTION_FAILURE = 26; // The number of times we failed 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. - static COUNTER_ALLOCATE_PACKET_BUFFER_FAILURE = 27 + static COUNTER_ALLOCATE_PACKET_BUFFER_FAILURE = 27; // The number of relayed unicast packets. - static COUNTER_RELAYED_UNICAST = 28 + static COUNTER_RELAYED_UNICAST = 28; // The number of times we dropped a packet due to reaching // the preset PHY to MAC queue limit (emMaxPhyToMacQueueLength). - static COUNTER_PHY_TO_MAC_QUEUE_LIMIT_REACHED = 29 + static COUNTER_PHY_TO_MAC_QUEUE_LIMIT_REACHED = 29; // The number of times we dropped a packet due to the // packet-validate library checking a packet and rejecting it // due to length or other formatting problems. - static COUNTER_PACKET_VALIDATE_LIBRARY_DROPPED_COUNT = 30 + static COUNTER_PACKET_VALIDATE_LIBRARY_DROPPED_COUNT = 30; // The number of times the NWK retry queue is full and a // new message failed to be added. - static COUNTER_TYPE_NWK_RETRY_OVERFLOW = 31 + static COUNTER_TYPE_NWK_RETRY_OVERFLOW = 31; // The number of times the PHY layer was unable to transmit // due to a failed CCA. - static COUNTER_PHY_CCA_FAIL_COUNT = 32 + static COUNTER_PHY_CCA_FAIL_COUNT = 32; // The number of times a NWK broadcast was dropped because // the broadcast table was full. - static COUNTER_BROADCAST_TABLE_FULL = 33 + static COUNTER_BROADCAST_TABLE_FULL = 33; // The number of low priority packet traffic arbitration requests. - static COUNTER_PTA_LO_PRI_REQUESTED = 34 + static COUNTER_PTA_LO_PRI_REQUESTED = 34; // The number of high priority packet traffic arbitration requests. - static COUNTER_PTA_HI_PRI_REQUESTED = 35 + static COUNTER_PTA_HI_PRI_REQUESTED = 35; // The number of low priority packet traffic arbitration requests denied. - static COUNTER_PTA_LO_PRI_DENIED = 36 + static COUNTER_PTA_LO_PRI_DENIED = 36; // The number of high priority packet traffic arbitration requests denied. - static COUNTER_PTA_HI_PRI_DENIED = 37 + static COUNTER_PTA_HI_PRI_DENIED = 37; // The number of aborted low priority packet traffic arbitration transmissions. - static COUNTER_PTA_LO_PRI_TX_ABORTED = 38 + static COUNTER_PTA_LO_PRI_TX_ABORTED = 38; // The number of aborted high priority packet traffic arbitration transmissions. - static COUNTER_PTA_HI_PRI_TX_ABORTED = 39 + static COUNTER_PTA_HI_PRI_TX_ABORTED = 39; // A placeholder giving the number of Ember counter types. - static COUNTER_TYPE_COUNT = 40 + static COUNTER_TYPE_COUNT = 40; } @@ -1530,7 +1530,7 @@ export class EmberJoinMethod extends basic.uint8_t { // the "permit joining" flag in the MAC Beacon. For mobile nodes this value // causes the device to use an Ember Mobile Node Join, which is functionally // equivalent to a MAC association. This value should be used by default. - static USE_MAC_ASSOCIATION = 0x0 + static USE_MAC_ASSOCIATION = 0x0; // For those networks where the "permit joining" flag is never turned on, // they 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 @@ -1539,18 +1539,18 @@ export class EmberJoinMethod extends basic.uint8_t { // determined by the ::EmberJoinDecision on the Trust Center returned by the // ::emberTrustCenterJoinHandler(). For a mobile node this value will cause // it to use an Ember Mobile node rejoin, which is functionally equivalent. - static USE_NWK_REJOIN = 0x1 + static USE_NWK_REJOIN = 0x1; // For those networks where the "permit joining" flag is never turned on, // they 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 ::USE_NWK_COMMISSIONING join method // below. - static USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2 + static USE_NWK_REJOIN_HAVE_NWK_KEY = 0x2; // For those 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. - static USE_NWK_COMMISSIONING = 0x3 + static USE_NWK_COMMISSIONING = 0x3; } @@ -1566,27 +1566,27 @@ export class EmberZdoConfigurationFlags extends basic.uint8_t { // handled by the EmberZNet stack. The stack will continue to handle the // request and send the appropriate ZDO response even if this configuration // option is enabled. - static APP_RECEIVES_SUPPORTED_ZDO_REQUESTS = 0x01 + static APP_RECEIVES_SUPPORTED_ZDO_REQUESTS = 0x01; // Set this flag in order to receive unsupported ZDO request messages via // the incomingMessageHandler callback. An unsupported ZDO request is one // that is not handled by the EmberZNet stack, other than to send a 'not // supported' ZDO response. If this configuration option is enabled, the // stack will no longer send any ZDO response, and it is the application's // responsibility to do so. - static APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS = 0x02 + static APP_HANDLES_UNSUPPORTED_ZDO_REQUESTS = 0x02; // Set this flag in order to receive the following ZDO request messages via // the incomingMessageHandler callback: SIMPLE_DESCRIPTOR_REQUEST, // MATCH_DESCRIPTORS_REQUEST, and ACTIVE_ENDPOINTS_REQUEST. If this // configuration option is enabled, the stack will no longer send any ZDO // response for these requests, and it is the application's responsibility // to do so. - static APP_HANDLES_ZDO_ENDPOINT_REQUESTS = 0x04 + static APP_HANDLES_ZDO_ENDPOINT_REQUESTS = 0x04; // Set this flag in order to receive the following ZDO request messages via // the incomingMessageHandler callback: BINDING_TABLE_REQUEST, BIND_REQUEST, // and UNBIND_REQUEST. If this configuration option is enabled, the stack // will no longer send any ZDO response for these requests, and it is the // application's responsibility to do so. - static APP_HANDLES_ZDO_BINDING_REQUESTS = 0x08 + static APP_HANDLES_ZDO_BINDING_REQUESTS = 0x08; } @@ -1596,11 +1596,11 @@ export class EmberConcentratorType extends basic.uint16_t { // 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. - static LOW_RAM_CONCENTRATOR = 0xFFF8 + static 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. - static HIGH_RAM_CONCENTRATOR = 0xFFF9 + static HIGH_RAM_CONCENTRATOR = 0xFFF9; } @@ -1608,17 +1608,17 @@ export class EmberZllState extends basic.uint16_t { // ZLL device state identifier. // No state. - static ZLL_STATE_NONE = 0x0000 + static ZLL_STATE_NONE = 0x0000; // The device is factory new. - static ZLL_STATE_FACTORY_NEW = 0x0001 + static ZLL_STATE_FACTORY_NEW = 0x0001; // The device is capable of assigning addresses to other devices. - static ZLL_STATE_ADDRESS_ASSIGNMENT_CAPABLE = 0x0002 + static ZLL_STATE_ADDRESS_ASSIGNMENT_CAPABLE = 0x0002; // The device is initiating a link operation. - static ZLL_STATE_LINK_INITIATOR = 0x0010 + static ZLL_STATE_LINK_INITIATOR = 0x0010; // The device is requesting link priority. - static ZLL_STATE_LINK_PRIORITY_REQUEST = 0x0020 + static ZLL_STATE_LINK_PRIORITY_REQUEST = 0x0020; // The device is on a non-ZLL network. - static ZLL_STATE_NON_ZLL_NETWORK = 0x0100 + static ZLL_STATE_NON_ZLL_NETWORK = 0x0100; } @@ -1626,18 +1626,18 @@ export class EmberZllKeyIndex extends basic.uint8_t { // ZLL key encryption algorithm enumeration. // Key encryption algorithm for use during development. - static ZLL_KEY_INDEX_DEVELOPMENT = 0x00 + static ZLL_KEY_INDEX_DEVELOPMENT = 0x00; // Key encryption algorithm shared by all certified devices. - static ZLL_KEY_INDEX_MASTER = 0x04 + static ZLL_KEY_INDEX_MASTER = 0x04; // Key encryption algorithm for use during development and certification. - static ZLL_KEY_INDEX_CERTIFICATION = 0x0F + static ZLL_KEY_INDEX_CERTIFICATION = 0x0F; } export class EzspZllNetworkOperation extends basic.uint8_t { // Differentiates among ZLL network operations. - static ZLL_FORM_NETWORK = 0x00 // ZLL form network command. - static ZLL_JOIN_TARGET = 0x01 // ZLL join target command. + static ZLL_FORM_NETWORK = 0x00; // ZLL form network command. + static ZLL_JOIN_TARGET = 0x01; // ZLL join target command. } @@ -1645,111 +1645,111 @@ export class EzspSourceRouteOverheadInformation extends basic.uint8_t { // Validates Source Route Overhead Information cached. // Ezsp source route overhead unknown - static SOURCE_ROUTE_OVERHEAD_UNKNOWN = 0xFF + static SOURCE_ROUTE_OVERHEAD_UNKNOWN = 0xFF; } export class EmberNetworkInitBitmask extends basic.uint16_t { // Bitmask options for emberNetworkInit(). // No options for Network Init - static NETWORK_INIT_NO_OPTIONS = 0x0000 + static NETWORK_INIT_NO_OPTIONS = 0x0000; // Save parent info (node ID and EUI64) in a token during joining/rejoin, // and restore on reboot. - static NETWORK_INIT_PARENT_INFO_IN_TOKEN = 0x0001 + static NETWORK_INIT_PARENT_INFO_IN_TOKEN = 0x0001; } export class EmberZDOCmd extends basic.uint16_t { // Device and Service Discovery Server Requests - static NWK_addr_req = 0x0000 - static IEEE_addr_req = 0x0001 - static Node_Desc_req = 0x0002 - static Power_Desc_req = 0x0003 - static Simple_Desc_req = 0x0004 - static Active_EP_req = 0x0005 - static Match_Desc_req = 0x0006 - static Complex_Desc_req = 0x0010 - static User_Desc_req = 0x0011 - static Discovery_Cache_req = 0x0012 - static Device_annce = 0x0013 - static User_Desc_set = 0x0014 - static System_Server_Discovery_req = 0x0015 - static Discovery_store_req = 0x0016 - static Node_Desc_store_req = 0x0017 - static Active_EP_store_req = 0x0019 - static Simple_Desc_store_req = 0x001A - static Remove_node_cache_req = 0x001B - static Find_node_cache_req = 0x001C - static Extended_Simple_Desc_req = 0x001D - static Extended_Active_EP_req = 0x001E - static Parent_annce = 0x001F + static NWK_addr_req = 0x0000; + static IEEE_addr_req = 0x0001; + static Node_Desc_req = 0x0002; + static Power_Desc_req = 0x0003; + static Simple_Desc_req = 0x0004; + static Active_EP_req = 0x0005; + static Match_Desc_req = 0x0006; + static Complex_Desc_req = 0x0010; + static User_Desc_req = 0x0011; + static Discovery_Cache_req = 0x0012; + static Device_annce = 0x0013; + static User_Desc_set = 0x0014; + static System_Server_Discovery_req = 0x0015; + static Discovery_store_req = 0x0016; + static Node_Desc_store_req = 0x0017; + static Active_EP_store_req = 0x0019; + static Simple_Desc_store_req = 0x001A; + static Remove_node_cache_req = 0x001B; + static Find_node_cache_req = 0x001C; + static Extended_Simple_Desc_req = 0x001D; + static Extended_Active_EP_req = 0x001E; + static Parent_annce = 0x001F; // Bind Management Server Services Responses - static End_Device_Bind_req = 0x0020 - static Bind_req = 0x0021 - static Unbind_req = 0x0022 + static End_Device_Bind_req = 0x0020; + static Bind_req = 0x0021; + static Unbind_req = 0x0022; // Network Management Server Services Requests // ... TODO optional stuff ... - static Mgmt_Lqi_req = 0x0031 - static Mgmt_Rtg_req = 0x0032 + static Mgmt_Lqi_req = 0x0031; + static Mgmt_Rtg_req = 0x0032; // ... TODO optional stuff ... - static Mgmt_Leave_req = 0x0034 - static Mgmt_Permit_Joining_req = 0x0036 - static Mgmt_NWK_Update_req = 0x0038 + static Mgmt_Leave_req = 0x0034; + static Mgmt_Permit_Joining_req = 0x0036; + static Mgmt_NWK_Update_req = 0x0038; // ... TODO optional stuff ... // Responses // Device and Service Discovery Server Responses - static NWK_addr_rsp = 0x8000 - static IEEE_addr_rsp = 0x8001 - static Node_Desc_rsp = 0x8002 - static Power_Desc_rsp = 0x8003 - static Simple_Desc_rsp = 0x8004 - static Active_EP_rsp = 0x8005 - static Match_Desc_rsp = 0x8006 - static Complex_Desc_rsp = 0x8010 - static User_Desc_rsp = 0x8011 - static Discovery_Cache_rsp = 0x8012 - static User_Desc_conf = 0x8014 - static System_Server_Discovery_rsp = 0x8015 - static Discovery_Store_rsp = 0x8016 - static Node_Desc_store_rsp = 0x8017 - static Power_Desc_store_rsp = 0x8018 - static Active_EP_store_rsp = 0x8019 - static Simple_Desc_store_rsp = 0x801A - static Remove_node_cache_rsp = 0x801B - static Find_node_cache_rsp = 0x801C - static Extended_Simple_Desc_rsp = 0x801D - static Extended_Active_EP_rsp = 0x801E - static Parent_annce_rsp = 0x801F + static NWK_addr_rsp = 0x8000; + static IEEE_addr_rsp = 0x8001; + static Node_Desc_rsp = 0x8002; + static Power_Desc_rsp = 0x8003; + static Simple_Desc_rsp = 0x8004; + static Active_EP_rsp = 0x8005; + static Match_Desc_rsp = 0x8006; + static Complex_Desc_rsp = 0x8010; + static User_Desc_rsp = 0x8011; + static Discovery_Cache_rsp = 0x8012; + static User_Desc_conf = 0x8014; + static System_Server_Discovery_rsp = 0x8015; + static Discovery_Store_rsp = 0x8016; + static Node_Desc_store_rsp = 0x8017; + static Power_Desc_store_rsp = 0x8018; + static Active_EP_store_rsp = 0x8019; + static Simple_Desc_store_rsp = 0x801A; + static Remove_node_cache_rsp = 0x801B; + static Find_node_cache_rsp = 0x801C; + static Extended_Simple_Desc_rsp = 0x801D; + static Extended_Active_EP_rsp = 0x801E; + static Parent_annce_rsp = 0x801F; // Bind Management Server Services Responses - static End_Device_Bind_rsp = 0x8020 - static Bind_rsp = 0x8021 - static Unbind_rsp = 0x8022 + static End_Device_Bind_rsp = 0x8020; + static Bind_rsp = 0x8021; + static Unbind_rsp = 0x8022; // ... TODO optional stuff ... // Network Management Server Services Responses - static Mgmt_Lqi_rsp = 0x8031 - static Mgmt_Rtg_rsp = 0x8032 + static Mgmt_Lqi_rsp = 0x8031; + static Mgmt_Rtg_rsp = 0x8032; // ... TODO optional stuff ... - static Mgmt_Leave_rsp = 0x8034 - static Mgmt_Permit_Joining_rsp = 0x8036 + static Mgmt_Leave_rsp = 0x8034; + static Mgmt_Permit_Joining_rsp = 0x8036; // ... TODO optional stuff ... - static Mgmt_NWK_Update_rsp = 0x8038 + static Mgmt_NWK_Update_rsp = 0x8038; } export class EzspDecisionBitmask extends basic.uint16_t { // EZSP Decision bitmask. // Disallow joins and rejoins. - static DEFAULT_CONFIGURATION = 0x0000 + static DEFAULT_CONFIGURATION = 0x0000; // Send the network key to all joining devices. - static ALLOW_JOINS = 0x0001 + static ALLOW_JOINS = 0x0001; // Send the network key to all rejoining devices. - static ALLOW_UNSECURED_REJOINS = 0x0002 + static ALLOW_UNSECURED_REJOINS = 0x0002; // Send the network key in the clear. - static SEND_KEY_IN_CLEAR = 0x0004 + static SEND_KEY_IN_CLEAR = 0x0004; // Do nothing for unsecured rejoins. - static IGNORE_UNSECURED_REJOINS = 0x0008 + static IGNORE_UNSECURED_REJOINS = 0x0008; // Allow joins if there is an entry in the transient key table. - static JOINS_USE_INSTALL_CODE_KEY = 0x0010 + static JOINS_USE_INSTALL_CODE_KEY = 0x0010; // Delay sending the network key to a new joining device. - static DEFER_JOINS = 0x0020 + static DEFER_JOINS = 0x0020; } \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 7bab99749a..2dd0b533b6 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -4,20 +4,20 @@ import * as named from './named'; export class EzspStruct { static serialize(cls: any, obj: any) { return Buffer.concat(cls._fields.map( (field:any[]) => { - let value = obj[field[0]]; - console.assert(field[1]) + const value = obj[field[0]]; + console.assert(field[1]); return field[1].serialize(field[1], value); - })) + })); } static deserialize(cls: any, data: Buffer) { - var r = new cls(); - for (let [field_name, field_type] of cls._fields) { + const r = new cls(); + for (const [field_name, field_type] of cls._fields) { let v; - [v, data] = field_type.deserialize(field_type, data) + [v, data] = field_type.deserialize(field_type, data); r[field_name] = v; } - return [r, data] + return [r, data]; } public toString():string { @@ -60,7 +60,7 @@ export class EmberNetworkParameters extends EzspStruct { // only be set at joining when using USE_NWK_COMMISSIONING as the join // method. ['channels', basic.uint32_t], - ] + ]; } export class EmberZigbeeNetwork extends EzspStruct { // The parameters of a ZigBee network. @@ -77,7 +77,7 @@ export class EmberZigbeeNetwork extends EzspStruct { ['stackProfile', basic.uint8_t], // The instance of the Network. ['nwkUpdateId', basic.uint8_t], - ] + ]; } export class EmberApsFrame extends EzspStruct { @@ -106,7 +106,7 @@ export class EmberApsFrame extends EzspStruct { ['groupId', basic.uint16_t], // The sequence number. ['sequence', basic.uint8_t], - ] + ]; } export class EmberBindingTableEntry extends EzspStruct { @@ -130,7 +130,7 @@ export class EmberBindingTableEntry extends EzspStruct { ['identifier', named.EmberEUI64], // The index of the network the binding belongs to. ['networkIndex', basic.uint8_t], - ] + ]; } export class EmberMulticastTableEntry extends EzspStruct { @@ -148,7 +148,7 @@ export class EmberMulticastTableEntry extends EzspStruct { ['endpoint', basic.uint8_t], // The network index of the network the entry is related to. ['networkIndex', basic.uint8_t], - ] + ]; } export class EmberKeyData extends EzspStruct { @@ -157,7 +157,7 @@ export class EmberKeyData extends EzspStruct { static _fields = [ // The key data. ['contents', basic.fixed_list(16, basic.uint8_t)], - ] + ]; } export class EmberCertificateData extends EzspStruct { @@ -166,7 +166,7 @@ export class EmberCertificateData extends EzspStruct { static _fields = [ // The certificate data. ['contents', basic.fixed_list(48, basic.uint8_t)], - ] + ]; } @@ -176,7 +176,7 @@ export class EmberPublicKeyData extends EzspStruct { static _fields = [ // The public key data. ['contents', basic.fixed_list(22, basic.uint8_t)], - ] + ]; } export class EmberPrivateKeyData extends EzspStruct { @@ -185,7 +185,7 @@ export class EmberPrivateKeyData extends EzspStruct { static _fields = [ // The private key data. ['contents', basic.fixed_list(21, basic.uint8_t)], - ] + ]; } export class EmberSmacData extends EzspStruct { @@ -193,7 +193,7 @@ export class EmberSmacData extends EzspStruct { static _fields = [ // The Shared Message Authentication Code data. ['contents', basic.fixed_list(16, basic.uint8_t)], - ] + ]; } export class EmberSignatureData extends EzspStruct { @@ -201,7 +201,7 @@ export class EmberSignatureData extends EzspStruct { static _fields = [ // The signature data. ['contents', basic.fixed_list(42, basic.uint8_t)], - ] + ]; } export class EmberCertificate283k1Data extends EzspStruct { @@ -209,7 +209,7 @@ export class EmberCertificate283k1Data extends EzspStruct { static _fields = [ // The 283k1 certificate data. ['contents', basic.fixed_list(74, basic.uint8_t)], - ] + ]; } export class EmberPublicKey283k1Data extends EzspStruct { @@ -217,7 +217,7 @@ export class EmberPublicKey283k1Data extends EzspStruct { static _fields = [ // The 283k1 public key data. ['contents', basic.fixed_list(37, basic.uint8_t)], - ] + ]; } export class EmberPrivateKey283k1Data extends EzspStruct { @@ -225,7 +225,7 @@ export class EmberPrivateKey283k1Data extends EzspStruct { static _fields = [ // The 283k1 private key data. ['contents', basic.fixed_list(36, basic.uint8_t)], - ] + ]; } export class EmberSignature283k1Data extends EzspStruct { @@ -233,7 +233,7 @@ export class EmberSignature283k1Data extends EzspStruct { static _fields = [ // The 283k1 signature data. ['contents', basic.fixed_list(72, basic.uint8_t)], - ] + ]; } export class EmberMessageDigest extends EzspStruct { @@ -241,7 +241,7 @@ export class EmberMessageDigest extends EzspStruct { static _fields = [ // The calculated digest of a message. ['contents', basic.fixed_list(16, basic.uint8_t)], - ] + ]; } export class EmberAesMmoHashContext extends EzspStruct { @@ -251,7 +251,7 @@ export class EmberAesMmoHashContext extends EzspStruct { ['result', basic.fixed_list(16, basic.uint8_t)], // The total length of the data that has been hashed so far. ['length', basic.uint32_t], - ] + ]; } export class EmberNeighborTableEntry extends EzspStruct { @@ -277,7 +277,7 @@ export class EmberNeighborTableEntry extends EzspStruct { ['age', basic.uint8_t], // The 8 byte EUI64 of the neighbor. ['longId', named.EmberEUI64], - ] + ]; } export class EmberRouteTableEntry extends EzspStruct { @@ -302,7 +302,7 @@ export class EmberRouteTableEntry extends EzspStruct { // needed [2], has been sent [1], or is no long needed [0] because a // source routed message from the concentrator has been received. ['routeRecordState', basic.uint8_t], - ] + ]; } export class EmberInitialSecurityState extends EzspStruct { @@ -340,7 +340,7 @@ export class EmberInitialSecurityState extends EzspStruct { // bit and leave this field alone. This field must be set when using // commissioning mode. ['preconfiguredTrustCenterEui64', named.EmberEUI64], - ] + ]; } export class EmberCurrentSecurityState extends EzspStruct { @@ -351,7 +351,7 @@ export class EmberCurrentSecurityState extends EzspStruct { ['bitmask', named.EmberCurrentSecurityBitmask], // The IEEE Address of the Trust Center device. ['trustCenterLongAddress', named.EmberEUI64], - ] + ]; } export class EmberKeyStruct extends EzspStruct { @@ -372,7 +372,7 @@ export class EmberKeyStruct extends EzspStruct { ['sequenceNumber', basic.uint8_t], // The IEEE address of the partner device also in possession of the key. ['partnerEUI64', named.EmberEUI64], - ] + ]; } export class EmberNetworkInitStruct extends EzspStruct { @@ -380,7 +380,7 @@ export class EmberNetworkInitStruct extends EzspStruct { static _fields = [ // Configuration options for network init. ['bitmask', named.EmberNetworkInitBitmask], - ] + ]; } export class EmberZllSecurityAlgorithmData extends EzspStruct { @@ -392,7 +392,7 @@ export class EmberZllSecurityAlgorithmData extends EzspStruct { ['responseId', basic.uint32_t], // Bitmask. ['bitmask', basic.uint16_t], - ] + ]; } export class EmberZllNetwork extends EzspStruct { @@ -416,7 +416,7 @@ export class EmberZllNetwork extends EzspStruct { ['totalGroupIdentifiers', basic.uint8_t], // RSSI correction value. ['rssiCorrection', basic.uint8_t], - ] + ]; } export class EmberZllInitialSecurityState extends EzspStruct { @@ -432,7 +432,7 @@ export class EmberZllInitialSecurityState extends EzspStruct { // The pre-configured link key used during classical ZigBee // commissioning. ['preconfiguredKey', EmberKeyData], - ] + ]; } export class EmberZllDeviceInfoRecord extends EzspStruct { @@ -450,7 +450,7 @@ export class EmberZllDeviceInfoRecord extends EzspStruct { ['version', basic.uint8_t], // Number of relevant group ids. ['groupIdCount', basic.uint8_t], - ] + ]; } export class EmberZllAddressAssignment extends EzspStruct { @@ -470,7 +470,7 @@ export class EmberZllAddressAssignment extends EzspStruct { ['freeGroupIdMin', named.EmberMulticastId], // Maximum free group id. ['freeGroupIdMax', named.EmberMulticastId], - ] + ]; } export class EmberTokTypeStackZllData extends EzspStruct { @@ -490,7 +490,7 @@ export class EmberTokTypeStackZllData extends EzspStruct { ['freeGroupIdMax', basic.uint16_t], // RSSI correction value. ['rssiCorrection', basic.uint8_t], - ] + ]; } export class EmberTokTypeStackZllSecurity extends EzspStruct { @@ -504,7 +504,7 @@ export class EmberTokTypeStackZllSecurity extends EzspStruct { ['encryptionKey', basic.fixed_list(16, basic.uint8_t)], // Preconfigured key. ['preconfiguredKey', basic.fixed_list(16, basic.uint8_t)], - ] + ]; } export class EmberRf4ceVendorInfo extends EzspStruct { @@ -515,7 +515,7 @@ export class EmberRf4ceVendorInfo extends EzspStruct { ['vendorId', basic.uint16_t], // The vendor string field shall contain the vendor string of the node. ['vendorString', basic.fixed_list(7, basic.uint8_t)], - ] + ]; } export class EmberRf4ceApplicationInfo extends EzspStruct { @@ -533,7 +533,7 @@ export class EmberRf4ceApplicationInfo extends EzspStruct { // The profile ID list field shall contain the list of profile // identifiers disclosed as supported by the node. ['profileIdList', basic.fixed_list(7, basic.uint8_t)], - ] + ]; } export class EmberRf4cePairingTableEntry extends EzspStruct { @@ -565,7 +565,7 @@ export class EmberRf4cePairingTableEntry extends EzspStruct { ['capabilities', basic.uint8_t], // Last MAC sequence number seen on this pairing link. ['lastSeqn', basic.uint8_t], - ] + ]; } export class EmberGpAddress extends EzspStruct { @@ -579,7 +579,7 @@ export class EmberGpAddress extends EzspStruct { ['applicationId', basic.uint8_t], // The GPD endpoint. ['endpoint', basic.uint8_t], - ] + ]; } export class EmberGpSinkListEntry extends EzspStruct { @@ -591,7 +591,7 @@ export class EmberGpSinkListEntry extends EzspStruct { ['sinkEUI', named.EmberEUI64], // The short address of the target sink. ['sinkNodeId', named.EmberNodeId], - ] + ]; } export class EmberNodeDescriptor extends EzspStruct { @@ -605,7 +605,7 @@ export class EmberNodeDescriptor extends EzspStruct { ['server_mask', basic.uint16_t], ['maximum_outgoing_transfer_size', basic.uint16_t], ['descriptor_capability_field', basic.uint8_t], - ] + ]; } @@ -617,7 +617,7 @@ export class EmberSimpleDescriptor extends EzspStruct { ['deviceversion', basic.uint8_t], ['inclusterlist', basic.LVList(basic.uint16_t)], ['outclusterlist', basic.LVList(basic.uint16_t)], - ] + ]; } export class EmberMultiAddress extends EzspStruct { @@ -625,7 +625,7 @@ export class EmberMultiAddress extends EzspStruct { ['addrmode', basic.uint8_t], // 0x3 ['ieee', named.EmberEUI64], ['endpoint', basic.uint8_t], - ] + ]; } export class EmberNeighbor extends EzspStruct { @@ -637,7 +637,7 @@ export class EmberNeighbor extends EzspStruct { ['permitjoining', basic.uint8_t], ['depth', basic.uint8_t], ['lqi', basic.uint8_t], - ] + ]; } export class EmberNeighbors extends EzspStruct { @@ -645,5 +645,5 @@ export class EmberNeighbors extends EzspStruct { ['entries', basic.uint8_t], ['startindex', basic.uint8_t], ['neighbors', basic.LVList(EmberNeighbor)], - ] + ]; } diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index b554362a72..ab1906a611 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -1,23 +1,23 @@ -import { EventEmitter } from 'events'; +import {EventEmitter} from 'events'; import SerialPort from 'serialport'; import net from 'net'; import SocketPortUtils from '../../socketPortUtils'; -import { Deferred, crc16ccitt } from './utils'; +import {Deferred, crc16ccitt} from './utils'; import * as stream from 'stream'; -import { Queue, Waitress } from '../../../utils'; +import {Queue, Waitress} from '../../../utils'; import Debug from "debug"; const debug = Debug('zigbee-herdsman:adapter:ezsp:uart'); -const FLAG = 0x7E // Marks end of frame -const ESCAPE = 0x7D // Indicates that the following byte is escaped -const CANCEL = 0x1A // Terminates a frame in progress -const XON = 0x11 // Resume transmission -const XOFF = 0x13 // Stop transmission -const SUBSTITUTE = 0x18 // Replaces a byte received with a low-level communication error -const STUFF = 0x20 -const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL] +const FLAG = 0x7E; // Marks end of frame +const ESCAPE = 0x7D; // Indicates that the following byte is escaped +const CANCEL = 0x1A; // Terminates a frame in progress +const XON = 0x11; // Resume transmission +const XOFF = 0x13; // Stop transmission +const SUBSTITUTE = 0x18; // Replaces a byte received with a low-level communication error +const STUFF = 0x20; +const RESERVED = [FLAG, ESCAPE, XON, XOFF, SUBSTITUTE, CANCEL]; const RANDOMIZE_START = 0x42; const RANDOMIZE_SEQ = 0xB8; @@ -90,7 +90,7 @@ class Parser extends stream.Transform { private unstuff(s: Buffer): Buffer { /* Unstuff (unescape) a string after receipt */ let escaped = false; - let out = Buffer.alloc(s.length); + const out = Buffer.alloc(s.length); let outIdx = 0; for (let idx = 0; idx < s.length; idx += 1) { const c = s[idx]; @@ -119,7 +119,7 @@ class Writer extends stream.Readable { public stuff(s: Iterable): Buffer { /* Byte stuff (escape) a string for transmission */ - let out = Buffer.alloc(256); + const out = Buffer.alloc(256); let outIdx = 0; for (const c of s) { if (RESERVED.includes(c)) { @@ -150,9 +150,9 @@ export class SerialDriver extends EventEmitter { private initialized: boolean; private resetDeferred: Deferred; private portType: 'serial' | 'socket'; - private sendSeq: number = 0; // next frame number to send - private recvSeq: number = 0; // next frame number to receive - private ackSeq: number = 0; // next number after the last accepted frame + private sendSeq = 0; // next frame number to send + private recvSeq = 0; // next frame number to receive + private ackSeq = 0; // next number after the last accepted frame private waitress: Waitress; private queue: Queue; @@ -258,35 +258,35 @@ export class SerialDriver extends EventEmitter { try { /* Frame receive handler */ switch (true) { - case ((data[0] & 0x80) === 0): - debug(`Recv DATA frame (${(data[0] & 0x70) >> 4},${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); - this.handleDATA(data); - break; + case ((data[0] & 0x80) === 0): + debug(`Recv DATA frame (${(data[0] & 0x70) >> 4},${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); + this.handleDATA(data); + break; - case ((data[0] & 0xE0) === 0x80): - debug(`Recv ACK frame (${data[0] & 0x07}): ${data.toString('hex')}`); - this.handleACK(data[0]); - break; - - case ((data[0] & 0xE0) === 0xA0): - debug(`Recv NAK frame (${data[0] & 0x07}): ${data.toString('hex')}`); - this.handleNAK(data[0]); - break; - - case (data[0] === 0xC0): - debug(`Recv RST frame: ${data.toString('hex')}`); - break; + case ((data[0] & 0xE0) === 0x80): + debug(`Recv ACK frame (${data[0] & 0x07}): ${data.toString('hex')}`); + this.handleACK(data[0]); + break; + + case ((data[0] & 0xE0) === 0xA0): + debug(`Recv NAK frame (${data[0] & 0x07}): ${data.toString('hex')}`); + this.handleNAK(data[0]); + break; + + case (data[0] === 0xC0): + debug(`Recv RST frame: ${data.toString('hex')}`); + break; - case (data[0] === 0xC1): - debug(`RSTACK frame: ${data.toString('hex')}`); - this.rstack_frame_received(data); - break; - - case (data[0] === 0xC2): - debug(`Error frame : ${data.toString('hex')}`); - break; - default: - debug("UNKNOWN FRAME RECEIVED: %r", data); + case (data[0] === 0xC1): + debug(`RSTACK frame: ${data.toString('hex')}`); + this.rstack_frame_received(data); + break; + + case (data[0] === 0xC2): + debug(`Error frame : ${data.toString('hex')}`); + break; + default: + debug("UNKNOWN FRAME RECEIVED: %r", data); } } catch (error) { @@ -345,7 +345,7 @@ export class SerialDriver extends EventEmitter { private rstack_frame_received(data: Buffer) { /* Reset acknowledgement frame receive handler */ - var code; + let code; this.sendSeq = 0; this.recvSeq = 0; try { @@ -371,8 +371,8 @@ export class SerialDriver extends EventEmitter { const sum = ctrlArr.concat(dataArr); - let crc = crc16ccitt(Buffer.from(sum), 65535); - let crcArr = [(crc >> 8), (crc % 256)]; + const crc = crc16ccitt(Buffer.from(sum), 65535); + const crcArr = [(crc >> 8), (crc % 256)]; return Buffer.concat([this.writer.stuff(sum.concat(crcArr)), Buffer.from([FLAG])]); } @@ -381,9 +381,9 @@ export class SerialDriver extends EventEmitter { Used only in data frames */ let rand = RANDOMIZE_START; - let out = Buffer.alloc(s.length); + const out = Buffer.alloc(s.length); let outIdx = 0; - for (let c of s){ + for (const c of s){ out.writeUInt8(c ^ rand, outIdx++); if ((rand % 2)) { rand = ((rand >> 1) ^ RANDOMIZE_SEQ); @@ -456,7 +456,7 @@ export class SerialDriver extends EventEmitter { } public sendDATA(data: Buffer) { - let seq = this.sendSeq; + const seq = this.sendSeq; this.sendSeq = ((seq + 1) % 8); // next const nextSeq = this.sendSeq; const ackSeq = this.recvSeq; @@ -498,7 +498,7 @@ export class SerialDriver extends EventEmitter { // } } - public waitFor(sequence: number, timeout: number = 10000) + public waitFor(sequence: number, timeout = 10000) : {start: () => {promise: Promise; ID: number}; ID: number} { return this.waitress.waitFor({sequence}, timeout); } diff --git a/src/adapter/ezsp/driver/utils/crc16ccitt.ts b/src/adapter/ezsp/driver/utils/crc16ccitt.ts index a8033214e8..2f3247d3fa 100644 --- a/src/adapter/ezsp/driver/utils/crc16ccitt.ts +++ b/src/adapter/ezsp/driver/utils/crc16ccitt.ts @@ -1,10 +1,10 @@ -import { Buffer } from 'buffer'; +import {Buffer} from 'buffer'; const createBuffer = Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow - ? Buffer.from - : // support for Node < 5.10 - (val: any) => Buffer.from(val); + ? Buffer.from + : // support for Node < 5.10 + (val: any) => Buffer.from(val); function defineCrc(model: string, calc: Function) { @@ -18,7 +18,7 @@ function defineCrc(model: string, calc: Function) { // Generated by `./pycrc.py --algorithm=table-driven --model=ccitt --generate=c` // prettier-ignore -let TABLE: number[] = [ +const TABLE: number[] = [ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index 0c3af7108c..15e26f6a21 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -1,6 +1,6 @@ import crc16ccitt from './crc16ccitt'; -import { EmberInitialSecurityState, EmberKeyData } from '../types/struct'; -import { EmberInitialSecurityBitmask, EmberEUI64 } from '../types/named'; +import {EmberInitialSecurityState, EmberKeyData} from '../types/struct'; +import {EmberInitialSecurityBitmask, EmberEUI64} from '../types/named'; if (!Symbol.asyncIterator){ (Symbol).asyncIterator = Symbol.for("Symbol.asyncIterator"); @@ -16,9 +16,9 @@ export class Deferred { constructor() { this.promise = new Promise((resolve, reject) => { - this._resolve = resolve - this._reject = reject - }) + this._resolve = resolve; + this._reject = reject; + }); } public resolve(value:T){ @@ -26,7 +26,7 @@ export class Deferred { this._resolve(value); } - public reject(value:T){ + public reject(value:T){ this._isResolved = true; this.reject(value); } @@ -60,41 +60,41 @@ export class AsyncQueue { consumer.resolve({ done: false, value - }) + }); } } else { return this.queue.push({ type: 'next', value - }) + }); } }, throw: (error: any) => { if (this.waiting.length > 0) { - const consumer = this.waiting.shift() - return consumer && consumer.reject(error) + const consumer = this.waiting.shift(); + return consumer && consumer.reject(error); } else { return this.queue.push({ value: error, type: 'error' - }) + }); } }, return: (value: T) => { if (this.waiting.length > 0) { - const consumer = this.waiting.shift() + const consumer = this.waiting.shift(); return consumer && consumer.resolve({ done: true, value - }) + }); } else { return this.queue.push({ value, type: 'return' - }) + }); } } - }) + }); } next(): Promise>{ @@ -109,28 +109,28 @@ export class AsyncQueue { return Promise.resolve({ done: true, value: item.value - }) + }); } else if (item.type === 'error') { - return Promise.reject(item.value) + return Promise.reject(item.value); } else { return Promise.resolve({ done: false, value: item.value - }) + }); } } else { // If there's nothing available then simply // give back a Promise immediately for when a value eventually // comes in const def = new Deferred>(); - this.waiting.push(def) + this.waiting.push(def); return def.promise; } } [Symbol.asyncIterator] = () => { - return this - } + return this; + }; } From cc615b1bb27d97548605c013f6e36676f9130ac8 Mon Sep 17 00:00:00 2001 From: mrG1K Date: Fri, 26 Feb 2021 07:56:58 +0300 Subject: [PATCH 58/63] eslint fix format --- src/adapter/ezsp/adapter/ezspAdapter.ts | 24 ++- src/adapter/ezsp/driver/commands.ts | 85 ++++++-- src/adapter/ezsp/driver/driver.ts | 66 ++++-- src/adapter/ezsp/driver/ezsp.ts | 36 +++- src/adapter/ezsp/driver/index.ts | 3 +- src/adapter/ezsp/driver/multicast.ts | 8 +- src/adapter/ezsp/driver/types/basic.ts | 20 +- src/adapter/ezsp/driver/types/index.ts | 218 +++++++++++++++++--- src/adapter/ezsp/driver/types/named.ts | 20 +- src/adapter/ezsp/driver/types/struct.ts | 5 +- src/adapter/ezsp/driver/uart.ts | 25 +-- src/adapter/ezsp/driver/utils/crc16ccitt.ts | 6 +- src/adapter/ezsp/driver/utils/index.ts | 28 +-- 13 files changed, 409 insertions(+), 135 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 2f743cf29b..376a880df4 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -7,9 +7,10 @@ import { } from '../../tstype'; import Debug from "debug"; import Adapter from '../../adapter'; + const debug = Debug("zigbee-herdsman:adapter:ezsp"); import {Driver} from '../driver'; -import { EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64, EmberStatus } from '../driver/types'; +import {EmberZDOCmd, EmberApsOption, uint16_t, EmberEUI64, EmberStatus} from '../driver/types'; import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl'; import * as Events from '../../events'; import {Waitress} from '../../../utils'; @@ -29,7 +30,7 @@ class EZSPAdapter extends Adapter { private waitress: Waitress; public constructor(networkOptions: NetworkOptions, - serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { + serialPortOptions: SerialPortOptions, backupPath: string, adapterOptions: AdapterOptions) { super(networkOptions, serialPortOptions, backupPath, adapterOptions); this.port = serialPortOptions; this.waitress = new Waitress( @@ -264,7 +265,10 @@ class EZSPAdapter extends Adapter { networkAddress, EmberZDOCmd.Node_Desc_req, EmberZDOCmd.Node_Desc_rsp, networkAddress ); - return {manufacturerCode: descriptor[2].manufacturer_code, type: (descriptor[1] == 0) ? 'Coordinator' : 'EndDevice'}; + return { + manufacturerCode: descriptor[2].manufacturer_code, + type: (descriptor[1] == 0) ? 'Coordinator' : 'EndDevice' + }; }); } @@ -313,7 +317,7 @@ class EZSPAdapter extends Adapter { ieeeAddr: string, networkAddress: number, endpoint: number, sourceEndpoint: number, zclFrame: ZclFrame, timeout: number, disableResponse: boolean, disableRecovery: boolean, responseAttempt: number, dataRequestAttempt: number, checkedNetworkAddress: boolean, discoveredRoute: boolean, assocRemove: boolean, - assocRestore: {ieeeadr: string, nwkaddr: number, noderelation: number} + assocRestore: { ieeeadr: string, nwkaddr: number, noderelation: number } ): Promise { debug('sendZclFrameToEndpointInternal %s:%i/%i (%i,%i,%i)', ieeeAddr, networkAddress, endpoint, responseAttempt, dataRequestAttempt, this.driver.queue.count()); @@ -337,8 +341,8 @@ class EZSPAdapter extends Adapter { frame.sourceEndpoint = sourceEndpoint || 0x01; frame.destinationEndpoint = endpoint; frame.groupId = 0; - frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY|EmberApsOption.APS_OPTION_RETRY; - + frame.options = EmberApsOption.APS_OPTION_ENABLE_ROUTE_DISCOVERY | EmberApsOption.APS_OPTION_RETRY; + const dataConfirmResult = await this.driver.request(networkAddress, frame, zclFrame.toBuffer()); if (response !== null) { @@ -382,7 +386,7 @@ class EZSPAdapter extends Adapter { const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); await this.driver.zdoRequest( destinationNetworkAddress, EmberZDOCmd.Bind_req, EmberZDOCmd.Bind_rsp, - ieee, sourceEndpoint, clusterID, + ieee, sourceEndpoint, clusterID, {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); }, destinationNetworkAddress); @@ -398,7 +402,7 @@ class EZSPAdapter extends Adapter { const ieeeDst = new EmberEUI64(destinationAddressOrGroup as string); await this.driver.zdoRequest( destinationNetworkAddress, EmberZDOCmd.Unbind_req, EmberZDOCmd.Unbind_rsp, - ieee, sourceEndpoint, clusterID, + ieee, sourceEndpoint, clusterID, {addrmode: 0x03, ieee: ieeeDst, endpoint: destinationEndpoint} ); }, destinationNetworkAddress); @@ -469,7 +473,7 @@ class EZSPAdapter extends Adapter { private waitForInternal( networkAddress: number, endpoint: number, transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {start: () => {promise: Promise}; cancel: () => void} { + ): { start: () => { promise: Promise }; cancel: () => void } { const payload = { address: networkAddress, endpoint, clusterID, commandIdentifier, transactionSequenceNumber, @@ -483,7 +487,7 @@ class EZSPAdapter extends Adapter { public waitFor( networkAddress: number, endpoint: number, frameType: FrameType, direction: Direction, transactionSequenceNumber: number, clusterID: number, commandIdentifier: number, timeout: number, - ): {promise: Promise; cancel: () => void} { + ): { promise: Promise; cancel: () => void } { const waiter = this.waitForInternal( networkAddress, endpoint, transactionSequenceNumber, clusterID, commandIdentifier, timeout, diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 074e16c0a6..b1ede648e5 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -8,24 +8,79 @@ import {/* Basic Types */ WordList, /* Named Types */ - EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberNodeId, - EmberPanId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, - EmberGpKeyType, SecureEzspRandomNumber, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, - EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, - EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, - EzspNetworkScanType, EmberJoinDecision, EmberKeyType, - EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, + EmberRf4ceTxOption, + EmberRf4ceNodeCapabilities, + EmberNodeId, + EmberPanId, + EmberEUI64, + EmberLibraryStatus, + SecureEzspSecurityType, + SecureEzspSecurityLevel, + EmberGpSecurityLevel, + EmberGpKeyType, + SecureEzspRandomNumber, + Bool, + EzspConfigId, + EzspValueId, + EzspExtendedValueId, + EzspPolicyId, + EzspDecisionId, + EzspMfgTokenId, + EzspStatus, + EmberStatus, + EmberEventUnits, + EmberNodeType, + EmberNetworkStatus, + EmberIncomingMessageType, + EmberOutgoingMessageType, + EmberMacPassthroughType, + EzspNetworkScanType, + EmberJoinDecision, + EmberKeyType, + EmberDeviceUpdate, + EmberKeyStatus, + EmberCounterType, EzspZllNetworkOperation, /* Structs */ - EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, - EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, - EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, - EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, - EmberKeyStruct, EmberNetworkInitStruct, EmberZllNetwork, EmberZllInitialSecurityState, - EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, + EmberNetworkParameters, + EmberZigbeeNetwork, + EmberApsFrame, + EmberBindingTableEntry, + EmberMulticastTableEntry, + EmberKeyData, + EmberCertificateData, + EmberPublicKeyData, + EmberPrivateKeyData, + EmberSmacData, + EmberSignatureData, + EmberCertificate283k1Data, + EmberPublicKey283k1Data, + EmberPrivateKey283k1Data, + EmberSignature283k1Data, + EmberMessageDigest, + EmberAesMmoHashContext, + EmberNeighborTableEntry, + EmberRouteTableEntry, + EmberInitialSecurityState, + EmberCurrentSecurityState, + EmberKeyStruct, + EmberNetworkInitStruct, + EmberZllNetwork, + EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, + EmberZllAddressAssignment, + EmberTokTypeStackZllData, + EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, + EmberRf4ceApplicationInfo, + EmberRf4cePairingTableEntry, + EmberGpAddress, + EmberGpSinkListEntry, + EmberNodeDescriptor, + EmberSimpleDescriptor, + EmberMultiAddress, + EmberNeighbors, } from './types'; export const COMMANDS = { diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index 30c66f5be8..dc7c4fb61f 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -4,7 +4,18 @@ import {EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, import {EventEmitter} from "events"; import {EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState} from './types/struct'; import {Deferred, ember_security} from './utils'; -import {EmberOutgoingMessageType, EmberEUI64, EmberJoinMethod, EmberDeviceUpdate, EzspValueId, EzspPolicyId, EzspDecisionBitmask, EzspMfgTokenId, EmberNetworkStatus, EmberKeyType} from './types/named'; +import { + EmberOutgoingMessageType, + EmberEUI64, + EmberJoinMethod, + EmberDeviceUpdate, + EzspValueId, + EzspPolicyId, + EzspDecisionBitmask, + EzspMfgTokenId, + EmberNetworkStatus, + EmberKeyType +} from './types/named'; import {Multicast} from './multicast'; import {Queue, Waitress} from '../../../utils'; import Debug from "debug"; @@ -17,7 +28,7 @@ const debug = { interface AddEndpointParameters { endpoint?: number, - profileId?: number, + profileId?: number, deviceId?: number, appFlags?: number, inputClusters?: number[], @@ -66,7 +77,7 @@ export class Driver extends EventEmitter { const ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); const version = await ezsp.version(); - + await ezsp.updateConfig(); await ezsp.updatePolicies(); @@ -79,19 +90,19 @@ export class Driver extends EventEmitter { //const count = await ezsp.getConfigurationValue(EzspConfigId.CONFIG_APS_UNICAST_MESSAGE_COUNT); //debug.log("APS_UNICAST_MESSAGE_COUNT is set to %s", count); - + //await this.addEndpoint({outputClusters: [0x0500]}); await this.addEndpoint({ - inputClusters: [0x0000, 0x0003, 0x0006, 0x000A, 0x0019, 0x001A, 0x0300], - outputClusters: [0x0000, 0x0003, 0x0004, 0x0005, 0x0006, 0x0008, 0x0020, - 0x0300, 0x0400, 0x0402, 0x0405, 0x0406, 0x0500, 0x0B01, 0x0B03, + inputClusters: [0x0000, 0x0003, 0x0006, 0x000A, 0x0019, 0x001A, 0x0300], + outputClusters: [0x0000, 0x0003, 0x0004, 0x0005, 0x0006, 0x0008, 0x0020, + 0x0300, 0x0400, 0x0402, 0x0405, 0x0406, 0x0500, 0x0B01, 0x0B03, 0x0B04, 0x0702, 0x1000, 0xFC01, 0xFC02] }); await this.addEndpoint({ endpoint: 242, profileId: 0xA10E, deviceId: 0x61, outputClusters: [0x0021] }); - + // getting MFG_STRING token const mfgName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_STRING); // getting MFG_BOARD_NAME token @@ -105,7 +116,13 @@ export class Driver extends EventEmitter { [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); const vers = `${major}.${minor}.${patch}.${special} build ${build}`; debug.log(`EmberZNet version: ${vers}`); - this.version = {product: this.ezsp.ezspV, majorrel: `${major}`, minorrel: `${minor}`, maintrel: `${patch} `, revision: vers}; + this.version = { + product: this.ezsp.ezspV, + majorrel: `${major}`, + minorrel: `${minor}`, + maintrel: `${patch} `, + revision: vers + }; if (await this.needsToBeInitialised(nwkOpt)) { const currentState = await ezsp.execCommand('networkState'); @@ -136,7 +153,7 @@ export class Driver extends EventEmitter { this.multicast = new Multicast(this); await this.multicast.startup([]); } - + private async needsToBeInitialised(options: TsType.NetworkOptions): Promise { let valid = true; valid = valid && (await this.ezsp.networkInit()); @@ -157,9 +174,9 @@ export class Driver extends EventEmitter { const panID = this.nwkOpt.panID; const extendedPanID = this.nwkOpt.extendedPanID; - const initial_security_state:EmberInitialSecurityState = ember_security(this.nwkOpt); + const initial_security_state: EmberInitialSecurityState = ember_security(this.nwkOpt); [status] = await this.ezsp.setInitialSecurityState(initial_security_state); - const parameters:EmberNetworkParameters = new EmberNetworkParameters(); + const parameters: EmberNetworkParameters = new EmberNetworkParameters(); parameters.panId = panID; parameters.extendedPanId = extendedPanID; parameters.radioTxPower = 20; @@ -168,7 +185,7 @@ export class Driver extends EventEmitter { parameters.nwkManagerId = 0; parameters.nwkUpdateId = 0; parameters.channels = 0x07FFF800; // all channels - + await this.ezsp.formNetwork(parameters); await this.ezsp.setValue(EzspValueId.VALUE_STACK_TOKEN_WRITING, 1); @@ -252,7 +269,7 @@ export class Driver extends EventEmitter { public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { try { - const seq = apsFrame.sequence+1; + const seq = apsFrame.sequence + 1; let eui64: EmberEUI64; if (typeof nwk !== 'number') { eui64 = nwk as EmberEUI64; @@ -278,9 +295,9 @@ export class Driver extends EventEmitter { return false; } } - + private nextTransactionID(): number { - this.transactionID = (this.transactionID+1) & 0xFF; + this.transactionID = (this.transactionID + 1) & 0xFF; return this.transactionID; } @@ -335,7 +352,7 @@ export class Driver extends EventEmitter { } } - public async permitJoining(seconds:number){ + public async permitJoining(seconds: number) { await this.ezsp.setPolicy(EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); return await this.ezsp.execCommand('permitJoining', seconds); } @@ -348,7 +365,14 @@ export class Driver extends EventEmitter { return this.ezsp.parse_frame_payload(name, obj); } - public async addEndpoint({endpoint=1, profileId=260, deviceId=0xBEEF, appFlags=0, inputClusters=[], outputClusters=[]}: AddEndpointParameters) { + public async addEndpoint({ + endpoint = 1, + profileId = 260, + deviceId = 0xBEEF, + appFlags = 0, + inputClusters = [], + outputClusters = [] + }: AddEndpointParameters) { const res = await this.ezsp.execCommand('addEndpoint', endpoint, profileId, @@ -363,7 +387,7 @@ export class Driver extends EventEmitter { } public waitFor(address: number, clusterId: number, sequence: number, timeout = 30000) - : {start: () => {promise: Promise; ID: number}; ID: number} { + : { start: () => { promise: Promise; ID: number }; ID: number } { return this.waitress.waitFor({address, clusterId, sequence}, timeout); } @@ -373,7 +397,7 @@ export class Driver extends EventEmitter { private waitressValidator(payload: EmberFrame, matcher: EmberWaitressMatcher): boolean { return (!matcher.address || payload.address === matcher.address) && - payload.frame.clusterId === matcher.clusterId && + payload.frame.clusterId === matcher.clusterId && payload.payload[0] === matcher.sequence; } -} \ No newline at end of file +} diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index af961df36e..0ec0fad020 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -3,7 +3,16 @@ import {SerialDriver} from './uart'; import {COMMANDS, ZDO_COMMANDS} from './commands'; import {Deferred} from './utils'; -import {EmberStatus, EmberOutgoingMessageType, EzspPolicyId, EzspDecisionId, EzspDecisionBitmask, EmberConcentratorType, EzspConfigId, EmberZdoConfigurationFlags} from './types/named'; +import { + EmberStatus, + EmberOutgoingMessageType, + EzspPolicyId, + EzspDecisionId, + EzspDecisionBitmask, + EmberConcentratorType, + EzspConfigId, + EmberZdoConfigurationFlags +} from './types/named'; import {EventEmitter} from 'events'; import {EmberApsFrame} from './types/struct'; import {Queue, Waitress} from '../../../utils'; @@ -49,7 +58,7 @@ export class Ezsp extends EventEmitter { this.queue = new Queue(); this.waitress = new Waitress( this.waitressValidator, this.waitressTimeoutFormatter); - + this.serialDriver = new SerialDriver(); this.serialDriver.on('received', this.onFrameReceived.bind(this)); } @@ -91,10 +100,15 @@ export class Ezsp extends EventEmitter { schema = cmd.outArgs; [result, data] = t.deserialize(data, schema); debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); - const handled = this.waitress.resolve({frameId: frame_id, frameName: frameName, sequence: sequence, payload: result}); + const handled = this.waitress.resolve({ + frameId: frame_id, + frameName: frameName, + sequence: sequence, + payload: result + }); if (!handled) this.emit('frame', frameName, ...result); - + if ((frame_id === 0)) { this.ezspV = result[0]; } @@ -172,7 +186,7 @@ export class Ezsp extends EventEmitter { console.assert(ret === EmberStatus.SUCCESS); return [ret, value]; } - + async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry) { let ret; [ret] = await this.execCommand('setMulticastTableEntry', index, entry); @@ -187,7 +201,7 @@ export class Ezsp extends EventEmitter { return [ret]; } - async getCurrentSecurityState(){ + async getCurrentSecurityState() { let ret, res; [ret, res] = await this.execCommand('getCurrentSecurityState'); console.assert(ret === EmberStatus.SUCCESS); @@ -238,7 +252,7 @@ export class Ezsp extends EventEmitter { [EzspConfigId.CONFIG_INDIRECT_TRANSMISSION_TIMEOUT, 7680], // 30000 [EzspConfigId.CONFIG_SOURCE_ROUTE_TABLE_SIZE, 16], // 61 - [EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16], + [EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE, 16], [EzspConfigId.CONFIG_ADDRESS_TABLE_SIZE, 16], // 8 [EzspConfigId.CONFIG_TRUST_CENTER_ADDRESS_CACHE_SIZE, 2], [EzspConfigId.CONFIG_SUPPORTED_NETWORKS, 1], @@ -279,7 +293,7 @@ export class Ezsp extends EventEmitter { await this.setPolicy(policy, value); } } - + public makeZDOframe(name: string, ...args: any[]): Buffer { let c, data, frame, cmd_id; c = (ZDO_COMMANDS)[name]; @@ -361,7 +375,7 @@ export class Ezsp extends EventEmitter { } public async setSourceRouting() { - const [res] = await this.execCommand('setConcentrator', + const [res] = await this.execCommand('setConcentrator', true, EmberConcentratorType.HIGH_RAM_CONCENTRATOR, MTOR_MIN_INTERVAL, @@ -376,9 +390,9 @@ export class Ezsp extends EventEmitter { } await this.execCommand('setSourceRouteDiscoveryMode', 1); } - + public waitFor(frameId: number, sequence: number, timeout = 30000) - : {start: () => {promise: Promise; ID: number}; ID: number} { + : { start: () => { promise: Promise; ID: number }; ID: number } { return this.waitress.waitFor({frameId, sequence}, timeout); } diff --git a/src/adapter/ezsp/driver/index.ts b/src/adapter/ezsp/driver/index.ts index bc64193aca..429a548f62 100644 --- a/src/adapter/ezsp/driver/index.ts +++ b/src/adapter/ezsp/driver/index.ts @@ -1,3 +1,4 @@ import {Ezsp} from './ezsp'; import {Driver} from './driver'; -export {Ezsp, Driver}; \ No newline at end of file + +export {Ezsp, Driver}; diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 98d2ad2709..5c3054242c 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -16,7 +16,7 @@ export class Multicast { private _multicast: any; private _available: Array; - constructor(driver: Driver){ + constructor(driver: Driver) { this.driver = driver; this._multicast = {}; this._available = []; @@ -54,7 +54,7 @@ export class Multicast { }); } - async subscribe(group_id: number):Promise { + async subscribe(group_id: number): Promise { if (this._multicast.indexOf(group_id) >= 0) { debug.log("%s is already subscribed", group_id); return EmberStatus.SUCCESS; @@ -62,7 +62,7 @@ export class Multicast { try { const idx = this._available.pop(); - const entry:EmberMulticastTableEntry = new EmberMulticastTableEntry(); + const entry: EmberMulticastTableEntry = new EmberMulticastTableEntry(); entry.endpoint = 1; entry.multicastId = group_id; entry.networkIndex = 0; @@ -91,4 +91,4 @@ export class Multicast { return EmberStatus.INDEX_OUT_OF_RANGE; } } -} \ No newline at end of file +} diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index b84399773b..97a8755ad1 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -107,6 +107,7 @@ export class LVBytes { } return Buffer.from([value.length].concat(value)); } + static deserialize(cls: any, data: Buffer) { let l, s; l = data.readIntLE(0, 1); @@ -120,6 +121,7 @@ export abstract class List { console.assert(((cls._length === null) || (cls.length === cls._length))); return Buffer.from(value.map(i => i.serialize(cls, i))); } + static deserialize(cls: any, data: Buffer): (any[] | Buffer)[] { let item; const r: any[] = []; @@ -138,6 +140,7 @@ class _LVList extends List { data = super.serialize(cls, value); return Buffer.from(head.concat(data)); } + static deserialize(cls: any, data: Buffer) { let item, length; const r: any[] = []; @@ -149,17 +152,20 @@ class _LVList extends List { return [r, data]; } } -export function list(itemtype: any) : List { + +export function list(itemtype: any): List { class ConreteList extends List { static itemtype = itemtype; } + return ConreteList; } -export function LVList(itemtype: any) : List { +export function LVList(itemtype: any): List { class LVList extends _LVList { static itemtype = itemtype; } + return LVList; } @@ -175,6 +181,7 @@ class _FixedList extends List { const data = value.map(i => cls.itemtype.serialize(cls.itemtype, i)[0]); return Buffer.from(data); } + static deserialize(cls: any, data: Buffer) { let item; const r: any[] = []; @@ -186,13 +193,14 @@ class _FixedList extends List { } } -export function fixed_list(length: number, itemtype: any) : { +export function fixed_list(length: number, itemtype: any): { new(): any; - deserialize(cls : any, data : Buffer) : any; + deserialize(cls: any, data: Buffer): any; } { - class FixedList extends _FixedList { + class FixedList extends _FixedList { static itemtype = itemtype; static _length = length; } + return FixedList; -} \ No newline at end of file +} diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index bfdf2d1196..38dd8a7251 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -14,25 +14,101 @@ import { } from './basic'; import { - NcpResetCode, EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberRf4ceApplicationCapabilities, EmberNodeId, - EmberPanId, EmberMulticastId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, - EmberGpKeyType, SecureEzspRandomNumber, SecureEzspSessionId, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, EzspEndpointFlags, - EmberConfigTxPowerMode, EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, - EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, - EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, - EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, - EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, + NcpResetCode, + EmberRf4ceTxOption, + EmberRf4ceNodeCapabilities, + EmberRf4ceApplicationCapabilities, + EmberNodeId, + EmberPanId, + EmberMulticastId, + EmberEUI64, + EmberLibraryStatus, + SecureEzspSecurityType, + SecureEzspSecurityLevel, + EmberGpSecurityLevel, + EmberGpKeyType, + SecureEzspRandomNumber, + SecureEzspSessionId, + Bool, + EzspConfigId, + EzspValueId, + EzspExtendedValueId, + EzspEndpointFlags, + EmberConfigTxPowerMode, + EzspPolicyId, + EzspDecisionId, + EzspMfgTokenId, + EzspStatus, + EmberStatus, + EmberEventUnits, + EmberNodeType, + EmberNetworkStatus, + EmberIncomingMessageType, + EmberOutgoingMessageType, + EmberMacPassthroughType, + EmberBindingType, + EmberApsOption, + EzspNetworkScanType, + EmberJoinDecision, + EmberInitialSecurityBitmask, + EmberCurrentSecurityBitmask, + EmberKeyType, + EmberKeyStructBitmask, + EmberDeviceUpdate, + EmberKeyStatus, + EmberCounterType, + EmberJoinMethod, + EmberZdoConfigurationFlags, + EmberConcentratorType, + EmberZllState, + EmberZllKeyIndex, + EzspZllNetworkOperation, + EzspSourceRouteOverheadInformation, + EmberNetworkInitBitmask, + EmberZDOCmd, } from './named'; import { - EzspStruct, EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, - EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, - EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, - EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, - EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, - EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, + EzspStruct, + EmberNetworkParameters, + EmberZigbeeNetwork, + EmberApsFrame, + EmberBindingTableEntry, + EmberMulticastTableEntry, + EmberKeyData, + EmberCertificateData, + EmberPublicKeyData, + EmberPrivateKeyData, + EmberSmacData, + EmberSignatureData, + EmberCertificate283k1Data, + EmberPublicKey283k1Data, + EmberPrivateKey283k1Data, + EmberSignature283k1Data, + EmberMessageDigest, + EmberAesMmoHashContext, + EmberNeighborTableEntry, + EmberRouteTableEntry, + EmberInitialSecurityState, + EmberCurrentSecurityState, + EmberKeyStruct, + EmberNetworkInitStruct, + EmberZllSecurityAlgorithmData, + EmberZllNetwork, + EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, + EmberZllAddressAssignment, + EmberTokTypeStackZllData, + EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, + EmberRf4ceApplicationInfo, + EmberRf4cePairingTableEntry, + EmberGpAddress, + EmberGpSinkListEntry, + EmberNodeDescriptor, + EmberSimpleDescriptor, + EmberMultiAddress, + EmberNeighbors, } from './struct'; export function deserialize(payload: any, schema: any[]) { @@ -65,22 +141,98 @@ export { WordList, /* Named Types */ - NcpResetCode, EmberRf4ceTxOption, EmberRf4ceNodeCapabilities, EmberRf4ceApplicationCapabilities, EmberNodeId, - EmberPanId, EmberMulticastId, EmberEUI64, EmberLibraryStatus, SecureEzspSecurityType, SecureEzspSecurityLevel, EmberGpSecurityLevel, - EmberGpKeyType, SecureEzspRandomNumber, SecureEzspSessionId, Bool, EzspConfigId, EzspValueId, EzspExtendedValueId, EzspEndpointFlags, - EmberConfigTxPowerMode, EzspPolicyId, EzspDecisionId, EzspMfgTokenId, EzspStatus, EmberStatus, EmberEventUnits, EmberNodeType, - EmberNetworkStatus, EmberIncomingMessageType, EmberOutgoingMessageType, EmberMacPassthroughType, EmberBindingType, EmberApsOption, - EzspNetworkScanType, EmberJoinDecision, EmberInitialSecurityBitmask, EmberCurrentSecurityBitmask, EmberKeyType, EmberKeyStructBitmask, - EmberDeviceUpdate, EmberKeyStatus, EmberCounterType, EmberJoinMethod, EmberZdoConfigurationFlags, EmberConcentratorType, EmberZllState, - EmberZllKeyIndex, EzspZllNetworkOperation, EzspSourceRouteOverheadInformation, EmberNetworkInitBitmask, EmberZDOCmd, + NcpResetCode, + EmberRf4ceTxOption, + EmberRf4ceNodeCapabilities, + EmberRf4ceApplicationCapabilities, + EmberNodeId, + EmberPanId, + EmberMulticastId, + EmberEUI64, + EmberLibraryStatus, + SecureEzspSecurityType, + SecureEzspSecurityLevel, + EmberGpSecurityLevel, + EmberGpKeyType, + SecureEzspRandomNumber, + SecureEzspSessionId, + Bool, + EzspConfigId, + EzspValueId, + EzspExtendedValueId, + EzspEndpointFlags, + EmberConfigTxPowerMode, + EzspPolicyId, + EzspDecisionId, + EzspMfgTokenId, + EzspStatus, + EmberStatus, + EmberEventUnits, + EmberNodeType, + EmberNetworkStatus, + EmberIncomingMessageType, + EmberOutgoingMessageType, + EmberMacPassthroughType, + EmberBindingType, + EmberApsOption, + EzspNetworkScanType, + EmberJoinDecision, + EmberInitialSecurityBitmask, + EmberCurrentSecurityBitmask, + EmberKeyType, + EmberKeyStructBitmask, + EmberDeviceUpdate, + EmberKeyStatus, + EmberCounterType, + EmberJoinMethod, + EmberZdoConfigurationFlags, + EmberConcentratorType, + EmberZllState, + EmberZllKeyIndex, + EzspZllNetworkOperation, + EzspSourceRouteOverheadInformation, + EmberNetworkInitBitmask, + EmberZDOCmd, /* Structs */ - EzspStruct, EmberNetworkParameters, EmberZigbeeNetwork, EmberApsFrame, EmberBindingTableEntry, EmberMulticastTableEntry, - EmberKeyData, EmberCertificateData, EmberPublicKeyData, EmberPrivateKeyData, EmberSmacData, EmberSignatureData, - EmberCertificate283k1Data, EmberPublicKey283k1Data, EmberPrivateKey283k1Data, EmberSignature283k1Data, EmberMessageDigest, - EmberAesMmoHashContext, EmberNeighborTableEntry, EmberRouteTableEntry, EmberInitialSecurityState, EmberCurrentSecurityState, - EmberKeyStruct, EmberNetworkInitStruct, EmberZllSecurityAlgorithmData, EmberZllNetwork, EmberZllInitialSecurityState, - EmberZllDeviceInfoRecord, EmberZllAddressAssignment, EmberTokTypeStackZllData, EmberTokTypeStackZllSecurity, - EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, EmberRf4cePairingTableEntry, EmberGpAddress, EmberGpSinkListEntry, - EmberNodeDescriptor, EmberSimpleDescriptor, EmberMultiAddress, EmberNeighbors, -}; \ No newline at end of file + EzspStruct, + EmberNetworkParameters, + EmberZigbeeNetwork, + EmberApsFrame, + EmberBindingTableEntry, + EmberMulticastTableEntry, + EmberKeyData, + EmberCertificateData, + EmberPublicKeyData, + EmberPrivateKeyData, + EmberSmacData, + EmberSignatureData, + EmberCertificate283k1Data, + EmberPublicKey283k1Data, + EmberPrivateKey283k1Data, + EmberSignature283k1Data, + EmberMessageDigest, + EmberAesMmoHashContext, + EmberNeighborTableEntry, + EmberRouteTableEntry, + EmberInitialSecurityState, + EmberCurrentSecurityState, + EmberKeyStruct, + EmberNetworkInitStruct, + EmberZllSecurityAlgorithmData, + EmberZllNetwork, + EmberZllInitialSecurityState, + EmberZllDeviceInfoRecord, + EmberZllAddressAssignment, + EmberTokTypeStackZllData, + EmberTokTypeStackZllSecurity, + EmberRf4ceVendorInfo, + EmberRf4ceApplicationInfo, + EmberRf4cePairingTableEntry, + EmberGpAddress, + EmberGpSinkListEntry, + EmberNodeDescriptor, + EmberSimpleDescriptor, + EmberMultiAddress, + EmberNeighbors, +}; diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index b9a196d18d..a10c2650be 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -18,14 +18,19 @@ export class NcpResetCode extends basic.uint8_t { export class EmberRf4ceTxOption extends basic.uint8_t { } + export class EmberRf4ceNodeCapabilities extends basic.uint8_t { } + export class EmberRf4ceApplicationCapabilities extends basic.uint8_t { } + export class EmberNodeId extends basic.uint16_t { } + export class EmberPanId extends basic.uint16_t { } + export class EmberMulticastId extends basic.uint8_t { } @@ -55,8 +60,9 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { data = arr[1] as Buffer; return [(r as number[]).reverse(), data]; } + static serialize(cls: any, value: number[] | EmberEUI64) { - if (value instanceof EmberEUI64){ + if (value instanceof EmberEUI64) { value = (value as EmberEUI64).value as number[]; } console.assert(cls._length === value.length); @@ -73,25 +79,33 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { } /* - + __hash__() { return hash(repr(this)); }*/ } + export class EmberLibraryStatus extends basic.uint8_t { } + export class SecureEzspSecurityType extends basic.uint32_t { } + export class SecureEzspSecurityLevel extends basic.uint8_t { } + export class EmberGpSecurityLevel extends basic.uint8_t { } + export class EmberGpKeyType extends basic.uint8_t { } + export class SecureEzspRandomNumber extends basic.uint64_t { } + export class SecureEzspSessionId extends basic.uint64_t { } + export class Bool extends basic.uint8_t { static false = 0x00; // An alias for zero, used for clarity. static true = 0x01; // An alias for one, used for clarity. @@ -1752,4 +1766,4 @@ export class EzspDecisionBitmask extends basic.uint16_t { static JOINS_USE_INSTALL_CODE_KEY = 0x0010; // Delay sending the network key to a new joining device. static DEFER_JOINS = 0x0020; -} \ No newline at end of file +} diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 2dd0b533b6..d95ca309cb 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -3,7 +3,7 @@ import * as named from './named'; export class EzspStruct { static serialize(cls: any, obj: any) { - return Buffer.concat(cls._fields.map( (field:any[]) => { + return Buffer.concat(cls._fields.map((field: any[]) => { const value = obj[field[0]]; console.assert(field[1]); return field[1].serialize(field[1], value); @@ -20,7 +20,7 @@ export class EzspStruct { return [r, data]; } - public toString():string { + public toString(): string { return `${this.constructor.name}: ${JSON.stringify(this)}`; } } @@ -62,6 +62,7 @@ export class EmberNetworkParameters extends EzspStruct { ['channels', basic.uint32_t], ]; } + export class EmberZigbeeNetwork extends EzspStruct { // The parameters of a ZigBee network. static _fields = [ diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index ab1906a611..90490c32b0 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -115,7 +115,8 @@ class Writer extends stream.Readable { this.push(buffer); } - public _read(): void {} + public _read(): void { + } public stuff(s: Iterable): Buffer { /* Byte stuff (escape) a string for transmission */ @@ -156,7 +157,7 @@ export class SerialDriver extends EventEmitter { private waitress: Waitress; private queue: Queue; - constructor(){ + constructor() { super(); this.initialized = false; this.queue = new Queue(); @@ -228,7 +229,7 @@ export class SerialDriver extends EventEmitter { this.parser.on('parsed', this.onParsed.bind(this)); return new Promise((resolve, reject): void => { - this.socketPort.on('connect', function() { + this.socketPort.on('connect', function () { debug('Socket connected'); }); @@ -262,7 +263,7 @@ export class SerialDriver extends EventEmitter { debug(`Recv DATA frame (${(data[0] & 0x70) >> 4},${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); this.handleDATA(data); break; - + case ((data[0] & 0xE0) === 0x80): debug(`Recv ACK frame (${data[0] & 0x07}): ${data.toString('hex')}`); this.handleACK(data[0]); @@ -276,7 +277,7 @@ export class SerialDriver extends EventEmitter { case (data[0] === 0xC0): debug(`Recv RST frame: ${data.toString('hex')}`); break; - + case (data[0] === 0xC1): debug(`RSTACK frame: ${data.toString('hex')}`); this.rstack_frame_received(data); @@ -288,7 +289,7 @@ export class SerialDriver extends EventEmitter { default: debug("UNKNOWN FRAME RECEIVED: %r", data); } - + } catch (error) { debug(`Error while parsing to ZpiObject '${error.stack}'`); } @@ -305,7 +306,7 @@ export class SerialDriver extends EventEmitter { this.recvSeq = (frmNum + 1) & 7; // next this.sendACK(this.recvSeq); this.handleACK(data[0]); - data = data.slice(1, (- 3)); + data = data.slice(1, (-3)); const frame = this.randomize(data); this.emit('received', frame); } @@ -383,7 +384,7 @@ export class SerialDriver extends EventEmitter { let rand = RANDOMIZE_START; const out = Buffer.alloc(s.length); let outIdx = 0; - for (const c of s){ + for (const c of s) { out.writeUInt8(c ^ rand, outIdx++); if ((rand % 2)) { rand = ((rand >> 1) ^ RANDOMIZE_SEQ); @@ -443,7 +444,7 @@ export class SerialDriver extends EventEmitter { this.initialized = false; this.emit('close'); } - + public isInitialized(): boolean { return this.initialized; } @@ -454,7 +455,7 @@ export class SerialDriver extends EventEmitter { debug(`Send ACK frame (${ackNum})`); this.writer.writeBuffer(ackFrame); } - + public sendDATA(data: Buffer) { const seq = this.sendSeq; this.sendSeq = ((seq + 1) % 8); // next @@ -499,7 +500,7 @@ export class SerialDriver extends EventEmitter { } public waitFor(sequence: number, timeout = 10000) - : {start: () => {promise: Promise; ID: number}; ID: number} { + : { start: () => { promise: Promise; ID: number }; ID: number } { return this.waitress.waitFor({sequence}, timeout); } @@ -510,4 +511,4 @@ export class SerialDriver extends EventEmitter { private waitressValidator(payload: EZSPPacket, matcher: EZSPPacketMatcher): boolean { return (payload.sequence === matcher.sequence); } -} \ No newline at end of file +} diff --git a/src/adapter/ezsp/driver/utils/crc16ccitt.ts b/src/adapter/ezsp/driver/utils/crc16ccitt.ts index 2f3247d3fa..1fc9f29346 100644 --- a/src/adapter/ezsp/driver/utils/crc16ccitt.ts +++ b/src/adapter/ezsp/driver/utils/crc16ccitt.ts @@ -1,8 +1,8 @@ import {Buffer} from 'buffer'; const createBuffer = - Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow - ? Buffer.from + Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow + ? Buffer.from : // support for Node < 5.10 (val: any) => Buffer.from(val); @@ -55,7 +55,7 @@ const TABLE: number[] = [ //if (typeof Int32Array !== 'undefined') TABLE = new Int32Array(TABLE); -const crc16ccitt = defineCrc('ccitt', function(buf: any, previous: any) { +const crc16ccitt = defineCrc('ccitt', function (buf: any, previous: any) { if (!Buffer.isBuffer(buf)) buf = createBuffer(buf); let crc = typeof previous !== 'undefined' ? ~~previous : 0xffff; diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index 15e26f6a21..95c3f9a773 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -2,7 +2,7 @@ import crc16ccitt from './crc16ccitt'; import {EmberInitialSecurityState, EmberKeyData} from '../types/struct'; import {EmberInitialSecurityBitmask, EmberEUI64} from '../types/named'; -if (!Symbol.asyncIterator){ +if (!Symbol.asyncIterator) { (Symbol).asyncIterator = Symbol.for("Symbol.asyncIterator"); } @@ -21,25 +21,25 @@ export class Deferred { }); } - public resolve(value:T){ + public resolve(value: T) { this._isResolved = true; this._resolve(value); } - public reject(value:T){ + public reject(value: T) { this._isResolved = true; this.reject(value); } - get isResolved(){ + get isResolved() { return this._isResolved; } - get isRejected(){ + get isRejected() { return this._isRejected; } - get isFullfilled(){ + get isFullfilled() { return this._isResolved || this._isRejected; } } @@ -97,12 +97,12 @@ export class AsyncQueue { }); } - next(): Promise>{ + next(): Promise> { if (this.queue.length > 1) { // If there are items available then simply put them // into the queue const item = this.queue.shift(); - if (!item){ + if (!item) { throw new Error('Working around TS strictNullCheck'); } if (item.type === 'return') { @@ -128,17 +128,17 @@ export class AsyncQueue { } } - [Symbol.asyncIterator] = () => { + [Symbol.asyncIterator] = () => { return this; }; } -function ember_security(config: any):EmberInitialSecurityState { +function ember_security(config: any): EmberInitialSecurityState { const isc: EmberInitialSecurityState = new EmberInitialSecurityState(); - isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | - EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | - EmberInitialSecurityBitmask.HAVE_NETWORK_KEY | + isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | + EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | + EmberInitialSecurityBitmask.HAVE_NETWORK_KEY | EmberInitialSecurityBitmask.PRECONFIGURED_NETWORK_KEY_MODE); isc.preconfiguredKey = new EmberKeyData(); isc.preconfiguredKey.contents = Buffer.from("ZigBeeAlliance09"); @@ -149,4 +149,4 @@ function ember_security(config: any):EmberInitialSecurityState { return isc; } -export {crc16ccitt, ember_security}; \ No newline at end of file +export {crc16ccitt, ember_security}; From c7c1549d8832fe28eaf1e666088e3ab70cfca346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 27 Feb 2021 16:23:07 +0300 Subject: [PATCH 59/63] fix --- src/adapter/ezsp/driver/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index b1ede648e5..966df247fd 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -83,7 +83,7 @@ import {/* Basic Types */ EmberNeighbors, } from './types'; -export const COMMANDS = { +export const COMMANDS: { [key: string]: [number, any[], any[]] } = { "version": [0, [uint8_t], [uint8_t, uint8_t, uint16_t] ], From 46f9413c6350371fb34978a5235201dab2dc1391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=BE=D0=B2=20=D0=98=D0=BB=D1=8C=D1=8F?= Date: Sat, 27 Feb 2021 19:11:48 +0300 Subject: [PATCH 60/63] fix lint error --- src/adapter/ezsp/driver/commands.ts | 19 ++- src/adapter/ezsp/driver/driver.ts | 74 ++++++---- src/adapter/ezsp/driver/ezsp.ts | 143 +++++++++++--------- src/adapter/ezsp/driver/multicast.ts | 15 +- src/adapter/ezsp/driver/test.ts | 37 ----- src/adapter/ezsp/driver/types/basic.ts | 74 +++++----- src/adapter/ezsp/driver/types/index.ts | 6 +- src/adapter/ezsp/driver/types/named.ts | 22 ++- src/adapter/ezsp/driver/types/struct.ts | 7 +- src/adapter/ezsp/driver/uart.ts | 38 +++--- src/adapter/ezsp/driver/utils/crc16ccitt.ts | 21 +-- src/adapter/ezsp/driver/utils/index.ts | 106 ++------------- 12 files changed, 238 insertions(+), 324 deletions(-) delete mode 100644 src/adapter/ezsp/driver/test.ts diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index 966df247fd..a8e90ea7d0 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -83,6 +83,7 @@ import {/* Basic Types */ EmberNeighbors, } from './types'; +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ export const COMMANDS: { [key: string]: [number, any[], any[]] } = { "version": [0, [uint8_t], [uint8_t, uint8_t, uint16_t] @@ -549,7 +550,8 @@ export const COMMANDS: { [key: string]: [number, any[], any[]] } = { "setPreinstalledCbkeData": [162, [EmberPublicKeyData, EmberCertificateData, EmberPrivateKeyData], [EmberStatus] ], - "setPreinstalledCbkeData283k1": [237, [EmberPublicKey283k1Data, EmberCertificate283k1Data, EmberPrivateKey283k1Data], + "setPreinstalledCbkeData283k1": [237, + [EmberPublicKey283k1Data, EmberCertificate283k1Data, EmberPrivateKey283k1Data], [EmberStatus] ], "mfglibStart": [131, [Bool], @@ -688,7 +690,8 @@ export const COMMANDS: { [key: string]: [number, any[], any[]] } = { [EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t, uint8_t] ], "rf4ceDiscoveryResponseHandler": [220, [], - [Bool, uint8_t, EmberPanId, EmberEUI64, uint8_t, EmberRf4ceVendorInfo, EmberRf4ceApplicationInfo, uint8_t, uint8_t] + [Bool, uint8_t, EmberPanId, EmberEUI64, uint8_t, EmberRf4ceVendorInfo, + EmberRf4ceApplicationInfo, uint8_t, uint8_t] ], "rf4ceEnableAutoDiscoveryResponse": [221, [uint16_t], [EmberStatus] @@ -732,7 +735,9 @@ export const COMMANDS: { [key: string]: [number, any[], any[]] } = { "rf4ceGetNetworkParameters": [244, [], [EmberStatus, EmberNodeType, EmberNetworkParameters] ], - "gpProxyTableProcessGpPairing": [201, [uint32_t, EmberGpAddress, uint8_t, uint16_t, uint16_t, uint16_t, fixed_list(8, uint8_t), EmberKeyData], + "gpProxyTableProcessGpPairing": [201, + [uint32_t, EmberGpAddress, uint8_t, uint16_t, uint16_t, uint16_t, + fixed_list(8, uint8_t), EmberKeyData], [] ], "dGpSend": [198, [Bool, Bool, EmberGpAddress, uint8_t, LVBytes, uint8_t, uint16_t], @@ -742,14 +747,16 @@ export const COMMANDS: { [key: string]: [number, any[], any[]] } = { [EmberStatus, uint8_t] ], "gpepIncomingMessageHandler": [197, [], - [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] + [EmberStatus, uint8_t, uint8_t, EmberGpAddress, EmberGpSecurityLevel, EmberGpKeyType, + Bool, Bool, uint32_t, uint8_t, uint32_t, EmberGpSinkListEntry, LVBytes] ], "changeSourceRouteHandler": [196, [], [EmberNodeId, EmberNodeId]], //Bool "setSourceRouteDiscoveryMode": [0x005A, [uint8_t,], [uint32_t,]], }; //// EmberZDOCmd -export const ZDO_COMMANDS = { +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +export const ZDO_COMMANDS: { [key: string]: [number, any[], any[]] } = { "Node_Desc_req": [0x0002, [uint8_t, EmberNodeId], [EmberStatus]], "Node_Desc_rsp": [0x8002, [EmberStatus, EmberNodeId, EmberNodeDescriptor], []], "Simple_Desc_req": [0x0004, [uint8_t, EmberNodeId, uint8_t], [EmberStatus]], @@ -765,5 +772,3 @@ export const ZDO_COMMANDS = { "Mgmt_Lqi_req": [0x0031, [uint8_t, uint8_t], [EmberStatus]], "Mgmt_Lqi_rsp": [0x8031, [uint8_t, EmberStatus, EmberNeighbors], [EmberStatus]], }; - -//# sourceMappingURL=commands.js.map diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index dc7c4fb61f..af616b9eb7 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,9 +1,9 @@ import * as TsType from './../../tstype'; import {Ezsp} from './ezsp'; -import {EmberStatus, EmberNodeType, EmberNodeId, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption} from './types'; +import {EmberStatus, EmberNodeType, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption} from './types'; import {EventEmitter} from "events"; import {EmberApsFrame, EmberNetworkParameters, EmberInitialSecurityState} from './types/struct'; -import {Deferred, ember_security} from './utils'; +import {ember_security} from './utils'; import { EmberOutgoingMessageType, EmberEUI64, @@ -12,7 +12,6 @@ import { EzspValueId, EzspPolicyId, EzspDecisionBitmask, - EzspMfgTokenId, EmberNetworkStatus, EmberKeyType } from './types/named'; @@ -72,11 +71,12 @@ export class Driver extends EventEmitter { this.waitressValidator, this.waitressTimeoutFormatter); } - public async startup(port: string, serialOpt: {}, nwkOpt: TsType.NetworkOptions) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public async startup(port: string, serialOpt: Record, nwkOpt: TsType.NetworkOptions): Promise { this.nwkOpt = nwkOpt; const ezsp = this.ezsp = new Ezsp(); await ezsp.connect(port, serialOpt); - const version = await ezsp.version(); + await ezsp.version(); await ezsp.updateConfig(); @@ -104,9 +104,10 @@ export class Driver extends EventEmitter { }); // getting MFG_STRING token - const mfgName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_STRING); + //const mfgName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_STRING); // getting MFG_BOARD_NAME token - const boardName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_BOARD_NAME); + //const boardName = await ezsp.execCommand('getMfgToken', EzspMfgTokenId.MFG_BOARD_NAME); + /* eslint-disable prefer-const */ let verInfo = await ezsp.getValue(EzspValueId.VALUE_VERSION_INFO); let build, major, minor, patch, special; [build, verInfo] = uint16_t.deserialize(uint16_t, verInfo); @@ -114,6 +115,7 @@ export class Driver extends EventEmitter { [minor, verInfo] = uint8_t.deserialize(uint8_t, verInfo); [patch, verInfo] = uint8_t.deserialize(uint8_t, verInfo); [special, verInfo] = uint8_t.deserialize(uint8_t, verInfo); + /* eslint-enable prefer-const */ const vers = `${major}.${minor}.${patch}.${special} build ${build}`; debug.log(`EmberZNet version: ${vers}`); this.version = { @@ -147,7 +149,7 @@ export class Driver extends EventEmitter { this.ieee = new EmberEUI64(ieee); debug.log('Network ready'); ezsp.on('frame', this.handleFrame.bind(this)); - this.handleNodeJoined(nwk, this.ieee, {}, {}, {}); + this.handleNodeJoined(nwk, this.ieee); debug.log(`EZSP nwk=${nwk}, IEEE=0x${this.ieee}`); this.multicast = new Multicast(this); @@ -167,7 +169,7 @@ export class Driver extends EventEmitter { return !valid; } - private async form_network() { + private async form_network(): Promise { let status; [status] = await this.ezsp.execCommand('clearKeyTable'); console.assert(status == EmberStatus.SUCCESS); @@ -193,7 +195,8 @@ export class Driver extends EventEmitter { await this.ezsp.execCommand('getKey', EmberKeyType.CURRENT_NETWORK_KEY); } - private handleFrame(frameName: string, ...args: any[]) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + private handleFrame(frameName: string, ...args: any[]): void { switch (true) { case (frameName === 'incomingMessageHandler'): { const [messageType, apsFrame, lqi, rssi, sender, bindingIndex, addressIndex, message] = args; @@ -209,10 +212,12 @@ export class Driver extends EventEmitter { break; } case (frameName === 'trustCenterJoinHandler'): { - if (args[2] === EmberDeviceUpdate.DEVICE_LEFT) { - this.handleNodeLeft.apply(this, args); + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + const [nwk, ieee, devUpdate, joinDecision, parent] = args; + if (devUpdate === EmberDeviceUpdate.DEVICE_LEFT) { + this.handleNodeLeft(nwk, ieee); } else { - this.handleNodeJoined.apply(this, args); + this.handleNodeJoined(nwk, ieee); } break; } @@ -241,17 +246,20 @@ export class Driver extends EventEmitter { } } - private handleRouteRecord(nwk: number, ieee: EmberEUI64 | number[], lqi: number, rssi: number, relays: any) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars */ + private handleRouteRecord(nwk: number, ieee: EmberEUI64 | number[], lqi: number, rssi: number, relays: any): void { debug.log(`handleRouteRecord: nwk=${nwk}, ieee=${ieee}, lqi=${lqi}, rssi=${rssi}, relays=${relays}`); // todo } - private handleRouteError(status: EmberStatus, nwk: number) { + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + private handleRouteError(status: EmberStatus, nwk: number): void { debug.log(`handleRouteError: number=${status}, nwk=${nwk}`); // todo } - private handleNodeLeft(nwk: number, ieee: EmberEUI64 | number[], ...args: any[]) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + private handleNodeLeft(nwk: number, ieee: EmberEUI64 | number[]): void { if (ieee && !(ieee instanceof EmberEUI64)) { ieee = new EmberEUI64(ieee); } @@ -259,7 +267,8 @@ export class Driver extends EventEmitter { this.emit('deviceLeft', [nwk, ieee]); } - private handleNodeJoined(nwk: number, ieee: EmberEUI64 | number[], deviceUpdate: any, joinDec: any, parentNwk: any) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + private handleNodeJoined(nwk: number, ieee: EmberEUI64 | number[]): void { if (ieee && !(ieee instanceof EmberEUI64)) { ieee = new EmberEUI64(ieee); } @@ -267,7 +276,9 @@ export class Driver extends EventEmitter { this.emit('deviceJoined', [nwk, ieee]); } - public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, data: Buffer, timeout = 30000): Promise { + public async request(nwk: number | EmberEUI64, apsFrame: EmberApsFrame, + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ + data: Buffer, timeout = 30000): Promise { try { const seq = apsFrame.sequence + 1; let eui64: EmberEUI64; @@ -289,7 +300,7 @@ export class Driver extends EventEmitter { } await this.ezsp.execCommand('setExtendedTimeout', eui64, true); - const v = await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); + await this.ezsp.sendUnicast(this.direct, nwk, apsFrame, seq, data); return true; } catch (e) { return false; @@ -301,7 +312,7 @@ export class Driver extends EventEmitter { return this.transactionID; } - public makeApsFrame(clusterId: number) { + public makeApsFrame(clusterId: number): EmberApsFrame { const frame = new EmberApsFrame(); frame.clusterId = clusterId; frame.profileId = 0; @@ -314,7 +325,9 @@ export class Driver extends EventEmitter { return frame; } - public async zdoRequest(networkAddress: number, requestCmd: EmberZDOCmd, responseCmd: EmberZDOCmd, ...args: any[]): Promise { + public async zdoRequest(networkAddress: number, requestCmd: EmberZDOCmd, + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + responseCmd: EmberZDOCmd, ...args: any[]): Promise { const requestName = EmberZDOCmd.valueName(EmberZDOCmd, requestCmd); const responseName = EmberZDOCmd.valueName(EmberZDOCmd, responseCmd); debug.log(`${requestName} params: ${[...args]}`); @@ -330,7 +343,7 @@ export class Driver extends EventEmitter { return result; } - public stop() { + public async stop(): Promise { return this.ezsp.close(); } @@ -344,6 +357,7 @@ export class Driver extends EventEmitter { } const value = await this.ezsp.execCommand('lookupEui64ByNodeId', nwk); if (value[0] === EmberStatus.SUCCESS) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ const eUI64 = new EmberEUI64(value[1] as any); this.eui64ToNodeId.set(eUI64.toString(), nwk); return eUI64; @@ -352,16 +366,20 @@ export class Driver extends EventEmitter { } } - public async permitJoining(seconds: number) { - await this.ezsp.setPolicy(EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); - return await this.ezsp.execCommand('permitJoining', seconds); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public async permitJoining(seconds: number): Promise { + await this.ezsp.setPolicy(EzspPolicyId.TRUST_CENTER_POLICY, + EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS); + return this.ezsp.execCommand('permitJoining', seconds); } - public makeZDOframe(name: string, ...args: any[]) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public makeZDOframe(name: string, ...args: any[]): Buffer { return this.ezsp.makeZDOframe(name, ...args); } - public parse_frame_payload(name: string, obj: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public parse_frame_payload(name: string, obj: Buffer): any[] { return this.ezsp.parse_frame_payload(name, obj); } @@ -372,7 +390,7 @@ export class Driver extends EventEmitter { appFlags = 0, inputClusters = [], outputClusters = [] - }: AddEndpointParameters) { + }: AddEndpointParameters): Promise { const res = await this.ezsp.execCommand('addEndpoint', endpoint, profileId, diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 0ec0fad020..2290b9d002 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -18,6 +18,7 @@ import {EmberApsFrame} from './types/struct'; import {Queue, Waitress} from '../../../utils'; import Debug from "debug"; + const debug = { error: Debug('zigbee-herdsman:adapter:ezsp:error'), log: Debug('zigbee-herdsman:adapter:ezsp:log'), @@ -33,6 +34,7 @@ type EZSPFrame = { sequence: number, frameId: number, frameName: string, + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ payload: any }; @@ -44,6 +46,7 @@ type EZSPWaitressMatcher = { export class Ezsp extends EventEmitter { ezspV = 4; cmdSeq = 0; // command sequence + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ COMMANDS_BY_ID = new Map(); private serialDriver: SerialDriver; private waitress: Waitress; @@ -52,7 +55,7 @@ export class Ezsp extends EventEmitter { constructor() { super(); for (const name in COMMANDS) { - const details = (COMMANDS)[name]; + const details = COMMANDS[name]; this.COMMANDS_BY_ID.set(details[0], {name, inArgs: details[1], outArgs: details[2]}); } this.queue = new Queue(); @@ -63,7 +66,7 @@ export class Ezsp extends EventEmitter { this.serialDriver.on('received', this.onFrameReceived.bind(this)); } - public async connect(path: string, options: {}) { + public async connect(path: string, options: Record): Promise { await this.serialDriver.connect(path, options); } @@ -71,7 +74,7 @@ export class Ezsp extends EventEmitter { return this.serialDriver.close(); } - private onFrameReceived(data: Buffer) { + private onFrameReceived(data: Buffer): void { /*Handle a received EZSP frame The protocol has taken care of UART specific framing etc, so we should @@ -79,7 +82,7 @@ export class Ezsp extends EventEmitter { data randomization removed. */ debug.log(`<=== Frame: ${data.toString('hex')}`); - let frame_id: number, result, schema, sequence; + let frame_id: number, result, sequence; if ((this.ezspV < 8)) { [sequence, frame_id, data] = [data[0], data[2], data.slice(3)]; } else { @@ -97,7 +100,7 @@ export class Ezsp extends EventEmitter { if (!cmd) throw new Error('Unrecognized command from FrameID' + frame_id); const frameName = cmd.name; debug.log("<=== Application frame %s (%s) received: %s", frame_id, frameName, data.toString('hex')); - schema = cmd.outArgs; + const schema = cmd.outArgs; [result, data] = t.deserialize(data, schema); debug.log(`<=== Application frame ${frame_id} (${frameName}) parsed: ${result}`); const handled = this.waitress.resolve({ @@ -114,7 +117,7 @@ export class Ezsp extends EventEmitter { } } - async version() { + async version(): Promise { const version = this.ezspV; const result = await this.command("version", version); if ((result[0] !== version)) { @@ -124,9 +127,9 @@ export class Ezsp extends EventEmitter { return result[0]; } - async networkInit() { - let fut: Deferred, v, st; - fut = new Deferred(); + async networkInit(): Promise { + const fut = new Deferred(); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); @@ -139,13 +142,15 @@ export class Ezsp extends EventEmitter { debug.log("Failure to init network:" + result); return false; } - v = await fut.promise; + const v = await fut.promise; return (v === EmberStatus.NETWORK_UP); } - async leaveNetwork() { - let fut: Deferred, v, st; - fut = new Deferred(); + async leaveNetwork(): Promise { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + let v; + const fut = new Deferred(); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); @@ -164,76 +169,72 @@ export class Ezsp extends EventEmitter { return v; } - async setConfigurationValue(configId: number, value: any) { - let ret; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async setConfigurationValue(configId: number, value: any): Promise { debug.log('Set %s = %s', EzspConfigId.valueToName(EzspConfigId, configId), value); - [ret] = await this.execCommand('setConfigurationValue', configId, value); + const [ret] = await this.execCommand('setConfigurationValue', configId, value); console.assert(ret === EmberStatus.SUCCESS); } - async getConfigurationValue(configId: number) { - let ret, value; + async getConfigurationValue(configId: number): Promise { debug.log('Get %s', EzspConfigId.valueToName(EzspConfigId, configId)); - [ret, value] = await this.execCommand('getConfigurationValue', configId); + const [ret, value] = await this.execCommand('getConfigurationValue', configId); console.assert(ret === EmberStatus.SUCCESS); debug.log('Got %s = %s', EzspConfigId.valueToName(EzspConfigId, configId), value); return value; } - async getMulticastTableEntry(index: number) { - let ret, value; - [ret, value] = await this.execCommand('getMulticastTableEntry', index); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async getMulticastTableEntry(index: number): Promise { + const [ret, value] = await this.execCommand('getMulticastTableEntry', index); console.assert(ret === EmberStatus.SUCCESS); return [ret, value]; } - async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry) { - let ret; - [ret] = await this.execCommand('setMulticastTableEntry', index, entry); + async setMulticastTableEntry(index: number, entry: t.EmberMulticastTableEntry): Promise { + const [ret] = await this.execCommand('setMulticastTableEntry', index, entry); console.assert(ret === EmberStatus.SUCCESS); return [ret]; } - async setInitialSecurityState(entry: t.EmberInitialSecurityState) { - let ret; - [ret] = await this.execCommand('setInitialSecurityState', entry); + async setInitialSecurityState(entry: t.EmberInitialSecurityState): Promise{ + const [ret] = await this.execCommand('setInitialSecurityState', entry); console.assert(ret === EmberStatus.SUCCESS); return [ret]; } - async getCurrentSecurityState() { - let ret, res; - [ret, res] = await this.execCommand('getCurrentSecurityState'); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async getCurrentSecurityState(): Promise { + const [ret, res] = await this.execCommand('getCurrentSecurityState'); console.assert(ret === EmberStatus.SUCCESS); return [ret, res]; } - async setValue(valueId: t.EzspValueId, value: any) { - let ret; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async setValue(valueId: t.EzspValueId, value: any): Promise { debug.log('Set %s = %s', t.EzspValueId.valueToName(t.EzspValueId, valueId), value); - [ret] = await this.execCommand('setValue', valueId, value); + const [ret] = await this.execCommand('setValue', valueId, value); console.assert(ret === EmberStatus.SUCCESS); return [ret]; } - async getValue(valueId: t.EzspValueId) { - let ret, value; + async getValue(valueId: t.EzspValueId): Promise { debug.log('Get %s', t.EzspValueId.valueToName(t.EzspValueId, valueId)); - [ret, value] = await this.execCommand('getValue', valueId); + const [ret, value] = await this.execCommand('getValue', valueId); console.assert(ret === EmberStatus.SUCCESS); debug.log('Got %s = %s', t.EzspValueId.valueToName(t.EzspValueId, valueId), value); return value; } - async setPolicy(policyId: EzspPolicyId, value: any) { - let ret; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async setPolicy(policyId: EzspPolicyId, value: any): Promise { debug.log('Set %s = %s', EzspPolicyId.valueToName(EzspPolicyId, policyId), value); - [ret] = await this.execCommand('setPolicy', policyId, value); + const [ret] = await this.execCommand('setPolicy', policyId, value); console.assert(ret === EmberStatus.SUCCESS); return [ret]; } - async updateConfig() { + async updateConfig(): Promise { const config = [ [EzspConfigId.CONFIG_FRAGMENT_DELAY_MS, 50], [EzspConfigId.CONFIG_TX_POWER_MODE, 3], @@ -273,19 +274,23 @@ export class Ezsp extends EventEmitter { } } - async updatePolicies() { + async updatePolicies(): Promise { // Set up the policies for what the NCP should do. const policies = [ - [EzspPolicyId.BINDING_MODIFICATION_POLICY, EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS], + [EzspPolicyId.BINDING_MODIFICATION_POLICY, + EzspDecisionId.CHECK_BINDING_MODIFICATIONS_ARE_VALID_ENDPOINT_CLUSTERS], [EzspPolicyId.UNICAST_REPLIES_POLICY, EzspDecisionId.HOST_WILL_NOT_SUPPLY_REPLY], [EzspPolicyId.POLL_HANDLER_POLICY, EzspDecisionId.POLL_HANDLER_IGNORE], - [EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK], - [EzspPolicyId.PACKET_VALIDATE_LIBRARY_POLICY, EzspDecisionId.PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED], + [EzspPolicyId.MESSAGE_CONTENTS_IN_CALLBACK_POLICY, + EzspDecisionId.MESSAGE_TAG_ONLY_IN_CALLBACK], + [EzspPolicyId.PACKET_VALIDATE_LIBRARY_POLICY, + EzspDecisionId.PACKET_VALIDATE_LIBRARY_CHECKS_ENABLED], [EzspPolicyId.ZLL_POLICY, EzspDecisionId.ALLOW_JOINS], [EzspPolicyId.TC_REJOINS_USING_WELL_KNOWN_KEY_POLICY, EzspDecisionId.ALLOW_JOINS], [EzspPolicyId.APP_KEY_REQUEST_POLICY, EzspDecisionId.DENY_APP_KEY_REQUESTS], - [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS | EzspDecisionBitmask.ALLOW_JOINS], + [EzspPolicyId.TRUST_CENTER_POLICY, EzspDecisionBitmask.IGNORE_UNSECURED_REJOINS + | EzspDecisionBitmask.ALLOW_JOINS], [EzspPolicyId.TC_KEY_REQUEST_POLICY, EzspDecisionId.ALLOW_TC_KEY_REQUESTS], ]; @@ -294,18 +299,18 @@ export class Ezsp extends EventEmitter { } } + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ public makeZDOframe(name: string, ...args: any[]): Buffer { - let c, data, frame, cmd_id; - c = (ZDO_COMMANDS)[name]; - data = t.serialize(args, c[1]); + const c = ZDO_COMMANDS[name]; + const data = t.serialize(args, c[1]); return data; } - private makeFrame(name: string, ...args: any[]) { - let c, data, frame, cmd_id; - c = (COMMANDS)[name]; - data = t.serialize(args, c[1]); - frame = [(this.cmdSeq & 255)]; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + private makeFrame(name: string, ...args: any[]): Buffer { + const c = COMMANDS[name]; + const data = t.serialize(args, c[1]); + const frame = [(this.cmdSeq & 255)]; if ((this.ezspV < 8)) { if ((this.ezspV >= 5)) { frame.push(0x00, 0xFF, 0x00, c[0]); @@ -313,18 +318,20 @@ export class Ezsp extends EventEmitter { frame.push(0x00, c[0]); } } else { - cmd_id = t.serialize([c[0]], [t.uint16_t]); + const cmd_id = t.serialize([c[0]], [t.uint16_t]); frame.push(0x00, 0x01, ...cmd_id); } return Buffer.concat([Buffer.from(frame), data]); } + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ private command(name: string, ...args: any[]): Promise { debug.log(`===> Send command ${name}: (${args})`); return this.queue.execute(async (): Promise => { const data = this.makeFrame(name, ...args); debug.log(`===> Send data ${name}: (${data.toString('hex')})`); - const c = (COMMANDS)[name]; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + const c = COMMANDS[name]; const waiter = this.waitFor(c[0], this.cmdSeq); this.cmdSeq = (this.cmdSeq + 1 % 256); this.serialDriver.sendDATA(data); @@ -333,15 +340,17 @@ export class Ezsp extends EventEmitter { }); } - async formNetwork(parameters: {}) { - let fut: Deferred, v, st; - fut = new Deferred(); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async formNetwork(...args: any[]): Promise { + let v; + const fut = new Deferred(); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ this.on('frame', (frameName: string, response: any) => { if ((frameName === "stackStatusHandler")) { fut.resolve(response); } }); - v = await this.command("formNetwork", parameters); + v = await this.command("formNetwork", ...args); if ((v[0] !== EmberStatus.SUCCESS)) { debug.log("Failure forming network:" + v); throw new Error(("Failure forming network:" + v)); @@ -354,6 +363,7 @@ export class Ezsp extends EventEmitter { return v; } + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ execCommand(name: string, ...args: any[]): any { if (Object.keys(COMMANDS).indexOf(name) < 0) { throw new Error('Unknown command: ' + name); @@ -361,20 +371,25 @@ export class Ezsp extends EventEmitter { return this.command(name, ...args); } - public parse_frame_payload(name: string, data: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public parse_frame_payload(name: string, data: Buffer): any[] { if (Object.keys(ZDO_COMMANDS).indexOf(name) < 0) { throw new Error('Unknown ZDO command: ' + name); } - const c = (ZDO_COMMANDS)[name]; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + const c = ZDO_COMMANDS[name]; const result = t.deserialize(data, c[1])[0]; return result; } - public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: EmberApsFrame, seq: number, data: Buffer) { + /* eslint-disable @typescript-eslint/no-explicit-any*/ + public sendUnicast(direct: EmberOutgoingMessageType, nwk: number, apsFrame: + EmberApsFrame, seq: number, data: Buffer): any { return this.execCommand('sendUnicast', direct, nwk, apsFrame, seq, data); } + /* eslint-enable @typescript-eslint/no-explicit-any*/ - public async setSourceRouting() { + public async setSourceRouting(): Promise { const [res] = await this.execCommand('setConcentrator', true, EmberConcentratorType.HIGH_RAM_CONCENTRATOR, diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 5c3054242c..60cbd751a1 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -1,7 +1,6 @@ import {Driver} from './driver'; -import {EzspConfigId, EmberZdoConfigurationFlags} from './types'; -import * as t from './types/basic'; -import {EmberStatus, EmberOutgoingMessageType, EmberMulticastId} from './types/named'; +import {EzspConfigId} from './types'; +import {EmberStatus} from './types/named'; import {EmberMulticastTableEntry} from './types/struct'; import Debug from "debug"; @@ -13,7 +12,9 @@ const debug = { export class Multicast { TABLE_SIZE = 16; private driver: Driver; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ private _multicast: any; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ private _available: Array; constructor(driver: Driver) { @@ -22,13 +23,12 @@ export class Multicast { this._available = []; } - private async _initialize() { + private async _initialize(): Promise { const size = await this.driver.ezsp.getConfigurationValue( EzspConfigId.CONFIG_MULTICAST_TABLE_SIZE ); for (let i = 0; i < size; i++) { - let st: any, entry: any; - [st, entry] = await this.driver.ezsp.getMulticastTableEntry(i); + const [st, entry] = await this.driver.ezsp.getMulticastTableEntry(i); if (st !== EmberStatus.SUCCESS) { debug.log("Couldn't get MulticastTableEntry #%s: %s", i, st); continue; @@ -42,7 +42,8 @@ export class Multicast { } } - async startup(enpoints: Array) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + async startup(enpoints: Array): Promise { return this.driver.queue.execute(async () => { await this._initialize(); for (const ep of enpoints) { diff --git a/src/adapter/ezsp/driver/test.ts b/src/adapter/ezsp/driver/test.ts deleted file mode 100644 index 38297dcb6e..0000000000 --- a/src/adapter/ezsp/driver/test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// import { ControllerApplication } from './application'; -// import { EmberApsFrame } from './types/struct'; -// import { EmberApsOption, EmberEUI64 } from './types/named'; - -// const application = new ControllerApplication(); - -// application.on('incomingMessage', ({ apsFrame, sender, senderEui64, message }: { apsFrame: EmberApsFrame, sender: number, senderEui64: EmberEUI64, message: Buffer }) => { -// console.log('incomingMessage', sender, senderEui64 && senderEui64.toString(), apsFrame, message); -// }); - -// application.startup('/dev/ttyUSB1', { -// baudRate: 57600, -// parity: 'none', -// stopBits: 1, -// xon: true, -// xoff: true -// }).then(async () => { - -// let localEui64 = await application.getLocalEUI64(); -// console.log('Local Eui64:', localEui64.toString()); - -// var res = await application.request(0xA329, { -// clusterId: 0x11, profileId: 0xC105, -// sequence: 1, -// sourceEndpoint: 0xE8, destinationEndpoint: 0xE8, -// options: EmberApsOption.APS_OPTION_FORCE_ROUTE_DISCOVERY | EmberApsOption.APS_OPTION_RETRY -// }, Buffer.from('\nTESTING!\n'), 0); - -// console.log('Sent=', res); - -// console.log('nodeID:', (await application.networkIdToEUI64(41769)).toString()); - -// var nwkParams = await application.getNetworkParameters(); -// console.log(nwkParams); -// }); - - diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index 97a8755ad1..7265191807 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -1,7 +1,8 @@ export class int_t { static _signed = true; - static serialize(cls: any, value: number) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, value: number): Buffer { const buffer = Buffer.alloc(cls._size, 0); if (cls._signed) { buffer.writeIntLE(value, 0, cls._size); @@ -11,11 +12,13 @@ export class int_t { return buffer; } - static deserialize(cls: any, data: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { return [cls._signed ? data.readIntLE(0, cls._size) : data.readUIntLE(0, cls._size), data.slice(cls._size)]; } - static valueToName(cls: any, value: any) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static valueToName(cls: any, value: any): string { for (const prop of Object.getOwnPropertyNames(cls)) { const desc = Object.getOwnPropertyDescriptor(cls, prop); if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { @@ -25,7 +28,8 @@ export class int_t { return ''; } - static valueName(cls: any, value: any) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static valueName(cls: any, value: any): string { for (const prop of Object.getOwnPropertyNames(cls)) { const desc = Object.getOwnPropertyDescriptor(cls, prop); if (desc !== undefined && desc.enumerable && desc.writable && value == desc.value) { @@ -80,26 +84,9 @@ export class uint64_t extends uint_t { static _size = 8; } -/* -export class Single extends number { - serialize() { - return struct.pack(" i.serialize(cls, i))); } - static deserialize(cls: any, data: Buffer): (any[] | Buffer)[] { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { let item; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ const r: any[] = []; while (data) { [item, data] = cls.itemtype.deserialize(cls.itemtype, data); @@ -134,15 +124,17 @@ export abstract class List { } class _LVList extends List { - static serialize(cls: any, value: any[]) { - let data, head; - head = [cls.length]; - data = super.serialize(cls, value); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, value: any[]): Buffer { + const head = [cls.length]; + const data = super.serialize(cls, value); return Buffer.from(head.concat(data)); } - static deserialize(cls: any, data: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { let item, length; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ const r: any[] = []; [length, data] = [data[0], data.slice(1)]; for (let i = 0; i < length; i++) { @@ -153,6 +145,7 @@ class _LVList extends List { } } +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ export function list(itemtype: any): List { class ConreteList extends List { static itemtype = itemtype; @@ -161,6 +154,7 @@ export function list(itemtype: any): List { return ConreteList; } +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ export function LVList(itemtype: any): List { class LVList extends _LVList { static itemtype = itemtype; @@ -170,20 +164,24 @@ export function LVList(itemtype: any): List { } export class WordList extends List { - static serialize(cls: any, value: any[]) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, value: any[]): Buffer { const data = value.map(i => Buffer.from(uint16_t.serialize(uint16_t, i))); return Buffer.concat(data); } } class _FixedList extends List { - static serialize(cls: any, value: any[]) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, value: any[]): Buffer { const data = value.map(i => cls.itemtype.serialize(cls.itemtype, i)[0]); return Buffer.from(data); } - static deserialize(cls: any, data: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { let item; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ const r: any[] = []; for (let i = 0; i < cls._length; i++) { [item, data] = cls.itemtype.deserialize(cls.itemtype, data); @@ -193,6 +191,7 @@ class _FixedList extends List { } } +/* eslint-disable @typescript-eslint/no-explicit-any*/ export function fixed_list(length: number, itemtype: any): { new(): any; deserialize(cls: any, data: Buffer): any; @@ -204,3 +203,4 @@ export function fixed_list(length: number, itemtype: any): { return FixedList; } +/* eslint-enable @typescript-eslint/no-explicit-any*/ \ No newline at end of file diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index 38dd8a7251..b618a83865 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -111,7 +111,8 @@ import { EmberNeighbors, } from './struct'; -export function deserialize(payload: any, schema: any[]) { +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +export function deserialize(payload: any, schema: any[]): any[] { const result = []; let value, data = payload; for (const type of schema) { @@ -121,7 +122,8 @@ export function deserialize(payload: any, schema: any[]) { return [result, data]; } -export function serialize(data: any[], schema: { serialize: Function }[]) { +/* eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types*/ +export function serialize(data: any[], schema: { serialize: Function }[]): Buffer { return Buffer.concat(schema.map((s, idx) => s.serialize(s, data[idx]))); } diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index a10c2650be..150fdef610 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -53,36 +53,34 @@ export class EmberEUI64 extends fixed_list(8, basic.uint8_t) { } } - static deserialize(cls: any, data: Buffer) { - let r; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { const arr = super.deserialize(cls, data); - r = arr[0]; + const r = arr[0]; data = arr[1] as Buffer; return [(r as number[]).reverse(), data]; } - static serialize(cls: any, value: number[] | EmberEUI64) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, value: number[] | EmberEUI64): Buffer { if (value instanceof EmberEUI64) { value = (value as EmberEUI64).value as number[]; } console.assert(cls._length === value.length); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ const val = (value as any[]).reverse().map(i => basic.uint8_t.serialize(basic.uint8_t, i)[0]); return Buffer.from(val); } - public get value() { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + public get value(): any { return this._value; } - public toString() { + public toString(): string { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ return Buffer.from(this._value as any).toString('hex'); } - - /* - - __hash__() { - return hash(repr(this)); - }*/ } export class EmberLibraryStatus extends basic.uint8_t { diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index d95ca309cb..04ab008257 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -2,7 +2,9 @@ import * as basic from './basic'; import * as named from './named'; export class EzspStruct { - static serialize(cls: any, obj: any) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static serialize(cls: any, obj: any): Buffer { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ return Buffer.concat(cls._fields.map((field: any[]) => { const value = obj[field[0]]; console.assert(field[1]); @@ -10,7 +12,8 @@ export class EzspStruct { })); } - static deserialize(cls: any, data: Buffer) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + static deserialize(cls: any, data: Buffer): any[] { const r = new cls(); for (const [field_name, field_type] of cls._fields) { let v; diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 90490c32b0..33d0c412bb 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -149,6 +149,7 @@ export class SerialDriver extends EventEmitter { private writer: Writer; private parser: Parser; private initialized: boolean; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ private resetDeferred: Deferred; private portType: 'serial' | 'socket'; private sendSeq = 0; // next frame number to send @@ -165,17 +166,16 @@ export class SerialDriver extends EventEmitter { this.waitressValidator, this.waitressTimeoutFormatter); } - async connect(path: string, options: {}) { + async connect(path: string, options: Record): Promise { this.portType = SocketPortUtils.isTcpPath(path) ? 'socket' : 'serial'; if (this.portType === 'serial') { await this.openSerialPort(path, options); } else { - await this.openSocketPort(path, options); + await this.openSocketPort(path); } } - private async openSerialPort(path: string, opt: {}): Promise { - // @ts-ignore + private async openSerialPort(path: string, opt: Record): Promise { const options = {baudRate: opt.baudRate, rtscts: false, autoOpen: false}; debug(`Opening SerialPort with ${path} and ${JSON.stringify(options)}`); @@ -213,7 +213,7 @@ export class SerialDriver extends EventEmitter { }); } - private async openSocketPort(path: string, options: {}): Promise { + private async openSocketPort(path: string): Promise { const info = SocketPortUtils.parseTcpPath(path); debug(`Opening TCP socket with ${info.host}:${info.port}`); @@ -235,7 +235,7 @@ export class SerialDriver extends EventEmitter { // eslint-disable-next-line const self = this; - this.socketPort.on('ready', async (error): Promise => { + this.socketPort.on('ready', async (): Promise => { debug('Socket ready'); // reset await this.reset(); @@ -260,7 +260,8 @@ export class SerialDriver extends EventEmitter { /* Frame receive handler */ switch (true) { case ((data[0] & 0x80) === 0): - debug(`Recv DATA frame (${(data[0] & 0x70) >> 4},${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); + debug(`Recv DATA frame (${(data[0] & 0x70) >> 4}, + ${data[0] & 0x07},${(data[0] & 0x08) >> 3}): ${data.toString('hex')}`); this.handleDATA(data); break; @@ -295,11 +296,11 @@ export class SerialDriver extends EventEmitter { } } - private handleDATA(data: Buffer) { + private handleDATA(data: Buffer): void { /* Data frame receive handler */ const frmNum = (data[0] & 0x70) >> 4; - const ackNum = data[0] & 0x07; - const reTx = (data[0] & 0x08) >> 3; + //const ackNum = data[0] & 0x07; + //const reTx = (data[0] & 0x08) >> 3; // if (seq !== this.recvSeq) { // debug('NAK-NAK'); // } @@ -311,7 +312,7 @@ export class SerialDriver extends EventEmitter { this.emit('received', frame); } - private handleACK(control: number) { + private handleACK(control: number): void { /* Handle an acknowledgement frame */ // next number after the last accepted frame this.ackSeq = control & 0x07; @@ -329,7 +330,7 @@ export class SerialDriver extends EventEmitter { // } } - private handleNAK(control: number) { + private handleNAK(control: number): void { /* Handle negative acknowledgment frame */ const nakNum = control & 0x07; const handled = this.waitress.reject({sequence: nakNum}, 'Recv NAK frame'); @@ -344,7 +345,7 @@ export class SerialDriver extends EventEmitter { // } } - private rstack_frame_received(data: Buffer) { + private rstack_frame_received(data: Buffer): void { /* Reset acknowledgement frame receive handler */ let code; this.sendSeq = 0; @@ -355,6 +356,7 @@ export class SerialDriver extends EventEmitter { code = NcpResetCode.ERROR_UNKNOWN_EM3XX_ERROR; } debug("RSTACK Version: %d Reason: %s frame: %s", data[1], code.toString(), data.toString('hex')); + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ if (NcpResetCode[code].toString() !== NcpResetCode.RESET_SOFTWARE.toString()) { return; } @@ -395,13 +397,13 @@ export class SerialDriver extends EventEmitter { return out; } - private makeDataFrame(data: Buffer, seq: number, rxmit: number, ackSeq: number) { + private makeDataFrame(data: Buffer, seq: number, rxmit: number, ackSeq: number): Buffer { /* Construct a data frame */ const control = (((seq << 4) | (rxmit << 3)) | ackSeq); return this.make_frame([control], this.randomize(data)); } - async reset() { + async reset(): Promise { // return this._gw.reset(); debug('uart reseting'); if ((this.resetDeferred)) { @@ -449,14 +451,14 @@ export class SerialDriver extends EventEmitter { return this.initialized; } - private sendACK(ackNum: number) { + private sendACK(ackNum: number): void { /* Construct a acknowledgement frame */ const ackFrame = this.make_frame([(0b10000000 | ackNum)]); debug(`Send ACK frame (${ackNum})`); this.writer.writeBuffer(ackFrame); } - public sendDATA(data: Buffer) { + public async sendDATA(data: Buffer): Promise { const seq = this.sendSeq; this.sendSeq = ((seq + 1) % 8); // next const nextSeq = this.sendSeq; @@ -469,7 +471,7 @@ export class SerialDriver extends EventEmitter { const waiter = this.waitFor(nextSeq); debug(`waiting (${nextSeq})`); this.writer.writeBuffer(pack); - await waiter.start().promise.catch(async (e) => { + await waiter.start().promise.catch(async () => { debug(`break waiting (${nextSeq})`); debug(`Can't send DATA frame (${seq},${ackSeq},0): ${data.toString('hex')}`); debug(`Resend DATA frame (${seq},${ackSeq},1): ${data.toString('hex')}`); diff --git a/src/adapter/ezsp/driver/utils/crc16ccitt.ts b/src/adapter/ezsp/driver/utils/crc16ccitt.ts index 1fc9f29346..6ce6087b33 100644 --- a/src/adapter/ezsp/driver/utils/crc16ccitt.ts +++ b/src/adapter/ezsp/driver/utils/crc16ccitt.ts @@ -1,14 +1,8 @@ import {Buffer} from 'buffer'; - -const createBuffer = - Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow - ? Buffer.from - : // support for Node < 5.10 - (val: any) => Buffer.from(val); - - -function defineCrc(model: string, calc: Function) { - const fn = (buf: any, previous: any) => calc(buf, previous) >>> 0; +/* eslint-disable-next-line @typescript-eslint/ban-types*/ +function defineCrc(model: string, calc: Function): Function { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ + const fn = (buf: any, previous: any): number => calc(buf, previous) >>> 0; fn.signed = calc; fn.unsigned = fn; fn.model = model; @@ -53,10 +47,9 @@ const TABLE: number[] = [ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 ]; -//if (typeof Int32Array !== 'undefined') TABLE = new Int32Array(TABLE); - -const crc16ccitt = defineCrc('ccitt', function (buf: any, previous: any) { - if (!Buffer.isBuffer(buf)) buf = createBuffer(buf); +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +const crc16ccitt = defineCrc('ccitt', function (buf: any, previous: any): number { + if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf); let crc = typeof previous !== 'undefined' ? ~~previous : 0xffff; diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index 95c3f9a773..e142e3fc1f 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -3,13 +3,16 @@ import {EmberInitialSecurityState, EmberKeyData} from '../types/struct'; import {EmberInitialSecurityBitmask, EmberEUI64} from '../types/named'; if (!Symbol.asyncIterator) { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ (Symbol).asyncIterator = Symbol.for("Symbol.asyncIterator"); } export class Deferred { public promise: Promise; + /* eslint-disable-next-line @typescript-eslint/ban-types*/ public _resolve: Function; + /* eslint-disable-next-line @typescript-eslint/ban-types*/ public _reject: Function; _isResolved = false; _isRejected = false; @@ -21,120 +24,31 @@ export class Deferred { }); } - public resolve(value: T) { + public resolve(value: T): void { this._isResolved = true; this._resolve(value); } - public reject(value: T) { + public reject(value: T): void { this._isResolved = true; this.reject(value); } - get isResolved() { + get isResolved(): boolean { return this._isResolved; } - get isRejected() { + get isRejected(): boolean { return this._isRejected; } - get isFullfilled() { + get isFullfilled(): boolean { return this._isResolved || this._isRejected; } } -//FROM: https://github.com/tc39/proposal-async-iteration/issues/99 -export class AsyncQueue { - private queue: Array<{ type: string, value: any }> = []; - private waiting: Array>> = []; - - constructor(initializer: Function) { - initializer({ - next: (value: T) => { - if (this.waiting.length > 0) { - // If anyone is waiting we'll just send them the value - // immediately - const consumer = this.waiting.shift(); - if (consumer) { - consumer.resolve({ - done: false, - value - }); - } - } else { - return this.queue.push({ - type: 'next', - value - }); - } - }, - throw: (error: any) => { - if (this.waiting.length > 0) { - const consumer = this.waiting.shift(); - return consumer && consumer.reject(error); - } else { - return this.queue.push({ - value: error, - type: 'error' - }); - } - }, - return: (value: T) => { - if (this.waiting.length > 0) { - const consumer = this.waiting.shift(); - return consumer && consumer.resolve({ - done: true, - value - }); - } else { - return this.queue.push({ - value, - type: 'return' - }); - } - } - }); - } - - next(): Promise> { - if (this.queue.length > 1) { - // If there are items available then simply put them - // into the queue - const item = this.queue.shift(); - if (!item) { - throw new Error('Working around TS strictNullCheck'); - } - if (item.type === 'return') { - return Promise.resolve({ - done: true, - value: item.value - }); - } else if (item.type === 'error') { - return Promise.reject(item.value); - } else { - return Promise.resolve({ - done: false, - value: item.value - }); - } - } else { - // If there's nothing available then simply - // give back a Promise immediately for when a value eventually - // comes in - const def = new Deferred>(); - this.waiting.push(def); - return def.promise; - } - } - - [Symbol.asyncIterator] = () => { - return this; - }; -} - - -function ember_security(config: any): EmberInitialSecurityState { +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +function ember_security(config: Record): EmberInitialSecurityState { const isc: EmberInitialSecurityState = new EmberInitialSecurityState(); isc.bitmask = (EmberInitialSecurityBitmask.HAVE_PRECONFIGURED_KEY | EmberInitialSecurityBitmask.TRUST_CENTER_GLOBAL_LINK_KEY | From d2989f95f1e75aeada687b2797feb45d222cbe52 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 1 Mar 2021 20:30:53 +0300 Subject: [PATCH 61/63] fix tests --- src/adapter/ezsp/adapter/ezspAdapter.ts | 2 +- test/controller.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 376a880df4..6dedd3b915 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -135,7 +135,7 @@ class EZSPAdapter extends Adapter { public static async isValidPath(path: string): Promise { // todo - return true; + return false; } public static async autoDetectPath(): Promise { diff --git a/test/controller.test.ts b/test/controller.test.ts index afafd8c891..56ddcac408 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -2902,7 +2902,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`)); + expect(error).toStrictEqual(new Error(`Adapter 'efr' does not exists, possible options: zstack, deconz, zigate, ezsp`)); }); it('Emit read from device', async () => { From b795b412adf33b32539ee319fc3e8d7d2b70305f Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Mon, 1 Mar 2021 21:30:47 +0300 Subject: [PATCH 62/63] test coverage for waitress --- test/utils.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/utils.test.ts b/test/utils.test.ts index 31559c8cfb..31003149c9 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -67,6 +67,25 @@ describe('Utils', () => { expect(error3).toStrictEqual(new Error("Timedout '5000'")); jest.useRealTimers(); + + // reject test + const wait1_ = waitress.waitFor(1, 5000).start(); + let error1_; + Wait(1000).then(() => {waitress.reject('one', 'drop');}); + try {await wait1_.promise} catch (e) { error1_ = e}; + expect(error1_).toStrictEqual(new Error("drop")); + + jest.useFakeTimers(); + const wait2_ = waitress.waitFor(2, 5000).start(); + let handled1 = waitress.reject('tree', 'drop'); + expect(handled1).toBe(false); + let error2_; + jest.runTimersToTime(6000); + try {await wait2_.promise} catch (e) { error2_ = e}; + expect(error2_).toStrictEqual(new Error("Timedout '5000'")); + let handled2 = waitress.reject('two', 'drop'); + expect(handled2).toBe(false); + jest.useRealTimers(); }); it('Test queue', async () => { From de3724341cffde51486c33117daba0ea1de30d59 Mon Sep 17 00:00:00 2001 From: Kirov Ilya Date: Tue, 2 Mar 2021 10:39:09 +0300 Subject: [PATCH 63/63] ignore test coverage --- src/adapter/ezsp/driver/commands.ts | 1 + src/adapter/ezsp/driver/driver.ts | 1 + src/adapter/ezsp/driver/ezsp.ts | 1 + src/adapter/ezsp/driver/index.ts | 1 + src/adapter/ezsp/driver/multicast.ts | 1 + src/adapter/ezsp/driver/types/basic.ts | 1 + src/adapter/ezsp/driver/types/index.ts | 1 + src/adapter/ezsp/driver/types/named.ts | 1 + src/adapter/ezsp/driver/types/struct.ts | 1 + src/adapter/ezsp/driver/uart.ts | 1 + src/adapter/ezsp/driver/utils/crc16ccitt.ts | 1 + src/adapter/ezsp/driver/utils/index.ts | 1 + 12 files changed, 12 insertions(+) diff --git a/src/adapter/ezsp/driver/commands.ts b/src/adapter/ezsp/driver/commands.ts index a8e90ea7d0..f7bf40444b 100644 --- a/src/adapter/ezsp/driver/commands.ts +++ b/src/adapter/ezsp/driver/commands.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import {/* Basic Types */ int8s, uint8_t, diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index af616b9eb7..458fcb3717 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import * as TsType from './../../tstype'; import {Ezsp} from './ezsp'; import {EmberStatus, EmberNodeType, uint16_t, uint8_t, EmberZDOCmd, EmberApsOption} from './types'; diff --git a/src/adapter/ezsp/driver/ezsp.ts b/src/adapter/ezsp/driver/ezsp.ts index 2290b9d002..d023b4b523 100644 --- a/src/adapter/ezsp/driver/ezsp.ts +++ b/src/adapter/ezsp/driver/ezsp.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import * as t from './types'; import {SerialDriver} from './uart'; import {COMMANDS, ZDO_COMMANDS} from './commands'; diff --git a/src/adapter/ezsp/driver/index.ts b/src/adapter/ezsp/driver/index.ts index 429a548f62..9a43190873 100644 --- a/src/adapter/ezsp/driver/index.ts +++ b/src/adapter/ezsp/driver/index.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import {Ezsp} from './ezsp'; import {Driver} from './driver'; diff --git a/src/adapter/ezsp/driver/multicast.ts b/src/adapter/ezsp/driver/multicast.ts index 60cbd751a1..7542db2bff 100644 --- a/src/adapter/ezsp/driver/multicast.ts +++ b/src/adapter/ezsp/driver/multicast.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import {Driver} from './driver'; import {EzspConfigId} from './types'; import {EmberStatus} from './types/named'; diff --git a/src/adapter/ezsp/driver/types/basic.ts b/src/adapter/ezsp/driver/types/basic.ts index 7265191807..310bba03ea 100644 --- a/src/adapter/ezsp/driver/types/basic.ts +++ b/src/adapter/ezsp/driver/types/basic.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ export class int_t { static _signed = true; diff --git a/src/adapter/ezsp/driver/types/index.ts b/src/adapter/ezsp/driver/types/index.ts index b618a83865..69453dd63b 100644 --- a/src/adapter/ezsp/driver/types/index.ts +++ b/src/adapter/ezsp/driver/types/index.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import { int8s, uint_t, diff --git a/src/adapter/ezsp/driver/types/named.ts b/src/adapter/ezsp/driver/types/named.ts index 150fdef610..fe0dfa487c 100644 --- a/src/adapter/ezsp/driver/types/named.ts +++ b/src/adapter/ezsp/driver/types/named.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import * as basic from './basic'; import {fixed_list} from './basic'; diff --git a/src/adapter/ezsp/driver/types/struct.ts b/src/adapter/ezsp/driver/types/struct.ts index 04ab008257..55a488701f 100644 --- a/src/adapter/ezsp/driver/types/struct.ts +++ b/src/adapter/ezsp/driver/types/struct.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import * as basic from './basic'; import * as named from './named'; diff --git a/src/adapter/ezsp/driver/uart.ts b/src/adapter/ezsp/driver/uart.ts index 33d0c412bb..3d79470739 100644 --- a/src/adapter/ezsp/driver/uart.ts +++ b/src/adapter/ezsp/driver/uart.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import {EventEmitter} from 'events'; import SerialPort from 'serialport'; import net from 'net'; diff --git a/src/adapter/ezsp/driver/utils/crc16ccitt.ts b/src/adapter/ezsp/driver/utils/crc16ccitt.ts index 6ce6087b33..bcab95adb4 100644 --- a/src/adapter/ezsp/driver/utils/crc16ccitt.ts +++ b/src/adapter/ezsp/driver/utils/crc16ccitt.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import {Buffer} from 'buffer'; /* eslint-disable-next-line @typescript-eslint/ban-types*/ function defineCrc(model: string, calc: Function): Function { diff --git a/src/adapter/ezsp/driver/utils/index.ts b/src/adapter/ezsp/driver/utils/index.ts index e142e3fc1f..27276b2f69 100644 --- a/src/adapter/ezsp/driver/utils/index.ts +++ b/src/adapter/ezsp/driver/utils/index.ts @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import crc16ccitt from './crc16ccitt'; import {EmberInitialSecurityState, EmberKeyData} from '../types/struct'; import {EmberInitialSecurityBitmask, EmberEUI64} from '../types/named';