From 7475bb8261c8f9db982bce5224e0b7d979d91be3 Mon Sep 17 00:00:00 2001 From: slugzero Date: Sun, 16 Apr 2023 17:41:14 +0200 Subject: [PATCH 1/4] Refactor: PendingRequest to class, make ZclFrame accessible from Endpoint --- src/controller/model/endpoint.ts | 117 +++++++++++++++++++++---------- test/controller.test.ts | 93 ++++++++++++------------ 2 files changed, 130 insertions(+), 80 deletions(-) diff --git a/src/controller/model/endpoint.ts b/src/controller/model/endpoint.ts index 7a04755746..c96a159014 100644 --- a/src/controller/model/endpoint.ts +++ b/src/controller/model/endpoint.ts @@ -70,10 +70,53 @@ interface ConfiguredReporting { reportableChange: number, } -interface PendingRequest { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ - func: () => Promise, resolve: (value: any) => any, reject: (error: any) => any, - sendWhen: 'active' | 'fastpoll', expires: number, lastError: Error +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +export class Request { + + private _func: (frame: Zcl.ZclFrame) => Promise; + frame: Zcl.ZclFrame; + expires: number; + sendWhen: SendRequestWhen; + private _resolveQueue: Array<(value: Type) => void>; + private _rejectQueue: Array <(error: Error) => void>; + private _lastError: Error; + constructor (func: (frame: Zcl.ZclFrame) => Promise, frame: Zcl.ZclFrame, timeout: number, + sendWhen?: SendRequestWhen, lastError?: Error, resolve?:(value: Type) => void, + reject?: (error: Error) => void) { + this._func = func; + this.frame = frame; + this.sendWhen = sendWhen ?? 'active', + this.expires = timeout + Date.now(); + this._resolveQueue = resolve === undefined ? + new Array<(value: Type) => void>() : new Array<(value: Type) => void>(resolve); + this._rejectQueue = reject === undefined ? + new Array<(error: Error) => void>() : new Array<(error: Error) => void>(reject); + this._lastError = lastError ?? Error("Request rejected before first send"); + } + + addCallbacks(resolve: (value: Type) => void, reject: (error: Error) => void): void { + this._resolveQueue.push(resolve); + this._rejectQueue.push(reject); + } + + reject(error?: Error): void { + this._rejectQueue.forEach(el => el(error ?? this._lastError)); + this._rejectQueue.length = 0; + } + + resolve(value: Type): void { + this._resolveQueue.forEach(el => el(value)); + this._resolveQueue.length = 0; + } + + async send(): Promise { + try { + return await this._func(this.frame); + } catch (error) { + this._lastError = error; + throw (error); + } + } } class Endpoint extends Entity { @@ -88,7 +131,8 @@ class Endpoint extends Entity { private _binds: BindInternal[]; private _configuredReportings: ConfiguredReportingInternal[]; public meta: KeyValue; - private pendingRequests: PendingRequest[]; + private pendingRequests: Request[]; + private postponedRequests: Request[]; private sendInProgress: boolean; // Getters/setters @@ -156,6 +200,8 @@ class Endpoint extends Entity { this._configuredReportings = configuredReportings; this.meta = meta; this.pendingRequests = []; + this.postponedRequests = []; + this.sendInProgress =false; } /** @@ -283,12 +329,12 @@ class Endpoint extends Entity { if (isExpired) { debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): discard after timeout. ` + `Size before: ${this.pendingRequests.length}`); - request.reject(request.lastError ?? new Error("Request timeout before first check-in")); + request.reject(); } return !isExpired; }); - const postponedRequests = []; + this.postponedRequests = []; debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): send pending requests (` + `${this.pendingRequests.length}, ${fastPolling})`); @@ -299,7 +345,7 @@ class Endpoint extends Entity { if ((fastPolling && request.sendWhen == 'fastpoll') || request.sendWhen == 'active') { try { - const result = await request.func(); + const result = await request.send(); debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): send success`); request.resolve(result); } catch (error) { @@ -309,62 +355,61 @@ class Endpoint extends Entity { } } else { - postponedRequests.push(request); + this.postponedRequests.push(request); } } - this.pendingRequests = postponedRequests; + this.pendingRequests = this.postponedRequests; + this.postponedRequests = []; this.sendInProgress = false; } - private async queueRequest(func: () => Promise, sendWhen: 'active' | 'fastpoll', - lastError?: Error): Promise { + private async queueRequest(request: Request): Promise { debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): Sending when active. ` + `Timeout ${this.getDevice().pendingRequestTimeout/1000} seconds`); return new Promise((resolve, reject): void => { - // Remove request from queue after timeout - const expires = this.getDevice().pendingRequestTimeout + Date.now(); - const request: PendingRequest = {func, resolve, reject, sendWhen, expires, lastError}; + request.addCallbacks(resolve, reject); this.pendingRequests.push(request); }); } - private async sendRequest(data: Zcl.ZclFrame, options: Options): Promise; - private async sendRequest( - data: Zcl.ZclFrame, options: Options, func: () => Promise): Promise; - private async sendRequest( - data: Zcl.ZclFrame, options: Options, func: () => Promise = (): Promise => { + private async sendRequest(frame: Zcl.ZclFrame, options: Options): Promise; + private async sendRequest(frame: Zcl.ZclFrame, options: Options, + func: (frame: Zcl.ZclFrame) => Promise): Promise; + private async sendRequest(frame: Zcl.ZclFrame, options: Options, + func: (d: Zcl.ZclFrame) => Promise = (d: Zcl.ZclFrame): Promise => { return Entity.adapter.sendZclFrameToEndpoint( - this.deviceIeeeAddress, this.deviceNetworkAddress, this.ID, data, options.timeout, + this.deviceIeeeAddress, this.deviceNetworkAddress, this.ID, d, options.timeout, options.disableResponse, options.disableRecovery, options.srcEndpoint) as Promise; }): Promise { const logPrefix = `Request Queue (${this.deviceIeeeAddress}/${this.ID}): `; + const request = new Request(func, frame, this.getDevice().pendingRequestTimeout, options.sendWhen); - // If we already have something queued, we queue directly to avoid - // messing up the ordering too much. - if (options.sendWhen !== 'immediate' && (this.hasPendingRequests() || this.sendInProgress)) { - debug.info(logPrefix + `queue request (${this.pendingRequests.length} / ${this.sendInProgress})))`); - return this.queueRequest(func, options.sendWhen); - } // send without queueing if sendWhen is 'immediate' or if this is a response - if (options.sendWhen === 'immediate' || (data.Header.frameControl.direction === Zcl.Direction.SERVER_TO_CLIENT)) - { + if (options.sendWhen === 'immediate' + || (frame.Header?.frameControl.direction === Zcl.Direction.SERVER_TO_CLIENT)) { if (this.getDevice().defaultSendRequestWhen !=='immediate') { - debug.info(logPrefix + `send ${data.getCommand().name} request, bypass queue on fail ` + + debug.info(logPrefix + `send ${frame.getCommand().name} request immediately ` + `(sendWhen=${options.sendWhen})`); } - return func(); + return request.send(); + } + // If we already have something queued, we queue directly to avoid + // messing up the ordering too much. + if (this.hasPendingRequests() || this.sendInProgress) { + debug.info(logPrefix + `queue request (${this.pendingRequests.length} / ${this.sendInProgress})))`); + return this.queueRequest(request); } try { debug.info(logPrefix + `send request (queue empty)`); - return await func(); + return await request.send(); } catch(error) { // If we got a failed transaction, the device is likely sleeping. // Queue for transmission later. debug.info(logPrefix + `queue request (transaction failed)`); - return this.queueRequest(func, options.sendWhen, error); + return this.queueRequest(request); } } @@ -820,13 +865,13 @@ class Endpoint extends Entity { debug.info(log); try { - await this.sendRequest(frame, options, async () => { + await this.sendRequest(frame, options, async (f) => { // Broadcast Green Power responses if (this.ID === 242) { - await Entity.adapter.sendZclFrameToAll(242, frame, 242); + await Entity.adapter.sendZclFrameToAll(242, f, 242); } else { await Entity.adapter.sendZclFrameToEndpoint( - this.deviceIeeeAddress, this.deviceNetworkAddress, this.ID, frame, options.timeout, + this.deviceIeeeAddress, this.deviceNetworkAddress, this.ID, f, options.timeout, options.disableResponse, options.disableRecovery, options.srcEndpoint ); } diff --git a/test/controller.test.ts b/test/controller.test.ts index 4200a7fca4..e934b7abbe 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -6,7 +6,8 @@ import {ZiGateAdapter} from "../src/adapter/zigate/adapter"; import equals from 'fast-deep-equal/es6'; import fs from 'fs'; import { ZclFrame } from "../src/zcl"; -import { Device, Group } from "../src/controller/model"; +import { Device, Group} from "../src/controller/model"; +import {Request} from "../src/controller/model/endpoint"; import * as Zcl from '../src/zcl'; import zclTransactionSequenceNumber from '../src/controller/helpers/zclTransactionSequenceNumber'; import {Adapter} from '../src/adapter'; @@ -585,6 +586,8 @@ describe('Controller', () => { inputClusters: [10], outputClusters: [11], pendingRequests: [], + postponedRequests: [], + sendInProgress: false, profileID: 2, ID: 1, meta: {}, @@ -598,6 +601,8 @@ describe('Controller', () => { inputClusters: [1], outputClusters: [0], pendingRequests: [], + postponedRequests: [], + sendInProgress: false, profileID: 3, meta: {}, ID: 2, @@ -797,7 +802,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); expect(equalsPartial(events.deviceJoined[0].device, {ID: 2, networkAddress: 129, ieeeAddr: '0x129'})).toBeTruthy(); expect(events.deviceInterview[0]).toStrictEqual({"device":{"_events":{},"_eventsCount":0,"meta": {}, "_skipDefaultResponse": false, "_skipTimeResponse": false, "_lastSeen": deepClone(Date.now()), "ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[],"_type":"Unknown","_ieeeAddr":"0x129","_interviewCompleted":false,"_interviewing":false,"_networkAddress":129},"status":"started"}); - const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"outputClusters":[2],"pendingRequests": [],"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"meta":{},"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; + const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"outputClusters":[2],"pendingRequests": [], "postponedRequests": [], "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"meta":{},"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; expect(events.deviceInterview[1]).toStrictEqual({"status":"successful","device":device}); expect(deepClone(controller.getDeviceByNetworkAddress(129))).toStrictEqual(device); expect(events.deviceInterview.length).toBe(2); @@ -815,7 +820,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); expect(equalsPartial(events.deviceJoined[0].device, {ID: 2, networkAddress: 129, ieeeAddr: '0x129'})).toBeTruthy(); expect(events.deviceInterview[0]).toStrictEqual({"device":{"meta": {}, "_skipDefaultResponse": false,"_events":{},"_eventsCount":0,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()), "ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[],"_ieeeAddr":"0x129","_interviewCompleted":false,"_interviewing":false,"_networkAddress":129,"_type":"Unknown"},"status":"started"}); - const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"meta":{},"outputClusters":[2],"pendingRequests": [],"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; + const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"meta":{},"outputClusters":[2],"pendingRequests": [], "postponedRequests": [], "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; expect(events.deviceInterview[1]).toStrictEqual({"status":"successful","device":device}); expect(deepClone(controller.getDeviceByIeeeAddr('0x129'))).toStrictEqual(device); expect(events.deviceInterview.length).toBe(2); @@ -1235,7 +1240,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "_binds": [], "_configuredReportings": [], "meta":{}, @@ -1273,7 +1278,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0,1], "outputClusters":[2], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1351,7 +1356,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1383,7 +1388,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1442,7 +1447,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1475,7 +1480,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1548,7 +1553,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1573,7 +1578,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1606,7 +1611,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "meta":{}, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", @@ -1679,7 +1684,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1716,7 +1721,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -2032,7 +2037,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":150, "deviceIeeeAddress":"0x150", "_binds": [], @@ -2089,7 +2094,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":151, "deviceIeeeAddress":"0x151", "_binds": [], @@ -2166,7 +2171,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -2199,7 +2204,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -2979,7 +2984,7 @@ describe('Controller', () => { const line = JSON.stringify({"id":3,"type":"EndDevice","ieeeAddr":"0x90fd9ffffe4b64ae","nwkAddr":19468,"manufId":4476,"manufName":"IKEA of Sweden","powerSource":"Battery","modelId":"TRADFRI remote control","epList":[1],"endpoints":{"1":{"profId":49246,"epId":1,"devId":2096,"inClusterList":[0,1,3,9,2821,4096],"outClusterList":[3,4,5,6,8,25,4096],"clusters":{}}},"appVersion":17,"stackVersion":87,"hwVersion":1,"dateCode":"20170302","swBuildId":"1.2.214","zclVersion":1,"interviewCompleted":true,"_id":"fJ5pmjqKRYbNvslK"}); fs.writeFileSync(options.databasePath, line + "\n"); await controller.start(); - const expected = {"ID": 3, "_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170302", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"clusters": {}, "ID": 1, "deviceID": 2096, "_binds": [], "_configuredReportings": [],"deviceIeeeAddress": "0x90fd9ffffe4b64ae", "deviceNetworkAddress": 19468, "inputClusters": [0, 1, 3, 9, 2821, 4096], "outputClusters": [3, 4, 5, 6, 8, 25, 4096], "pendingRequests": [], "profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x90fd9ffffe4b64ae", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {}, "_modelID": "TRADFRI remote control", "_networkAddress": 19468, "_powerSource": "Battery", "_softwareBuildID": "1.2.214", "_stackVersion": 87, "_type": "EndDevice", "_zclVersion": 1} + const expected = {"ID": 3, "_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170302", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"clusters": {}, "ID": 1, "deviceID": 2096, "_binds": [], "_configuredReportings": [],"deviceIeeeAddress": "0x90fd9ffffe4b64ae", "deviceNetworkAddress": 19468, "inputClusters": [0, 1, 3, 9, 2821, 4096], "outputClusters": [3, 4, 5, 6, 8, 25, 4096], "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x90fd9ffffe4b64ae", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {}, "_modelID": "TRADFRI remote control", "_networkAddress": 19468, "_powerSource": "Battery", "_softwareBuildID": "1.2.214", "_stackVersion": 87, "_type": "EndDevice", "_zclVersion": 1} expect(deepClone(controller.getDeviceByIeeeAddr("0x90fd9ffffe4b64ae"))).toStrictEqual(expected); }); @@ -3300,10 +3305,10 @@ describe('Controller', () => { fs.writeFileSync(options.databasePath, database); await controller.start(); expect((controller.getDevices()).length).toBe(4); - expect(deepClone(controller.getDeviceByIeeeAddr('0x123'))).toStrictEqual({"ID":1,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"_events":{},"_eventsCount":0,"inputClusters":[],"outputClusters":[],"profileID":260,"ID":1,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":257,"ID":2,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":261,"ID":3,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":263,"ID":4,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":264,"ID":5,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":265,"ID":6,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"deviceID":1024,"inputClusters":[],"outputClusters":[1280],"profileID":260,"ID":11,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]}],"_ieeeAddr":"0x123","_interviewCompleted":false,"_interviewing":false,"_lastSeen":null,"_manufacturerID":0,"_networkAddress":0,"_type":"Coordinator","_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x000b57fffec6a5b2'))).toStrictEqual({"ID": 3,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170331", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests":[], "profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x000b57fffec6a5b2", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {"reporting": 1}, "_modelID": "TRADFRI bulb E27 WS opal 980lm", "_networkAddress": 40369, "_powerSource": "Mains (single phase)", "_softwareBuildID": "1.2.217", "_stackVersion": 87, "_type": "Router", "_zclVersion": 1}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45517'))).toStrictEqual({"ID":4,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{"genBasic":{"dir":{"value":3},"attributes":{"modelId":"RWL021"}}},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[{"type":"endpoint","endpointID":1,"deviceIeeeAddr":"0x000b57fffec6a5b2"}],"_configuredReportings":[{"cluster":1,"attrId":0,"minRepIntval":1,"maxRepIntval":20,"repChange":2}],"meta":{},"pendingRequests":[]},{"deviceID":12,"inputClusters":[0,1,3,15,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45517","_interviewCompleted":true,"_interviewing":false,"_lastSeen":123,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6538,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45518'))).toStrictEqual({"ID":6,"_checkinInterval":123456,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":123456000,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"meta":{},"pendingRequests":[]},{"deviceID":12,"inputClusters":[0,1,3,15,32,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45518","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6536,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x123'))).toStrictEqual({"ID":1,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"_events":{},"_eventsCount":0,"inputClusters":[],"outputClusters":[],"profileID":260,"ID":1,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":257,"ID":2,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":261,"ID":3,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":263,"ID":4,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":264,"ID":5,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":265,"ID":6,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":1024,"inputClusters":[],"outputClusters":[1280],"profileID":260,"ID":11,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x123","_interviewCompleted":false,"_interviewing":false,"_lastSeen":null,"_manufacturerID":0,"_networkAddress":0,"_type":"Coordinator","_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x000b57fffec6a5b2'))).toStrictEqual({"ID": 3,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170331", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests":[],"postponedRequests":[],"sendInProgress":false,"profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x000b57fffec6a5b2", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {"reporting": 1}, "_modelID": "TRADFRI bulb E27 WS opal 980lm", "_networkAddress": 40369, "_powerSource": "Mains (single phase)", "_softwareBuildID": "1.2.217", "_stackVersion": 87, "_type": "Router", "_zclVersion": 1}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45517'))).toStrictEqual({"ID":4,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{"genBasic":{"dir":{"value":3},"attributes":{"modelId":"RWL021"}}},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[{"type":"endpoint","endpointID":1,"deviceIeeeAddr":"0x000b57fffec6a5b2"}],"_configuredReportings":[{"cluster":1,"attrId":0,"minRepIntval":1,"maxRepIntval":20,"repChange":2}],"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45517","_interviewCompleted":true,"_interviewing":false,"_lastSeen":123,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6538,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45518'))).toStrictEqual({"ID":6,"_checkinInterval":123456,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":123456000,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,32,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45518","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6536,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); expect((await controller.getGroups({})).length).toBe(2); const group1 = controller.getGroupByID(1); @@ -3311,7 +3316,7 @@ describe('Controller', () => { expect(deepClone(group1)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 2, "groupID": 1, "_members": [], "meta": {}}); const group2 = controller.getGroupByID(2); group2._members = Array.from(group2._members); - expect(deepClone(group2)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 5, "groupID": 2, "_members": [{"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "_events":{},"_eventsCount":0,"deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests": [], "profileID": 49246}], "meta": {}}); + expect(deepClone(group2)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 5, "groupID": 2, "_members": [{"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "_events":{},"_eventsCount":0,"deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID": 49246}], "meta": {}}); }); it('Shouldnt load device from group databaseentry', async () => { @@ -3461,7 +3466,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3503,7 +3508,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3572,7 +3577,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3615,7 +3620,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], + "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3890,7 +3895,7 @@ describe('Controller', () => { }); expect(events.deviceJoined.length).toBe(1); - expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"inputClusters":[],"outputClusters":[],"pendingRequests":[],"ID":242,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": [],"_events":{},"_eventsCount":0,"meta":{}}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); + expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"inputClusters":[],"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": [],"_events":{},"_eventsCount":0,"meta":{}}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); expect(events.deviceInterview.length).toBe(1); expect(deepClone(events.deviceInterview[0])).toStrictEqual({"status":"successful","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); expect((controller.getDeviceByIeeeAddr('0x000000000046f4fe')).networkAddress).toBe(0xf4fe); @@ -3919,7 +3924,7 @@ describe('Controller', () => { }); expect(events.message.length).toBe(1); - const expected = {"type":"commandNotification","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[],"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []}],"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[],"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []},"data":{"options":0,"srcID":0x46f4fe,"frameCounter":228,"commandID":34,"payloadSize":255,"commandFrame":{}},"linkquality":50,"groupID":1,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"commandNotification","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []}],"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []},"data":{"options":0,"srcID":0x46f4fe,"frameCounter":228,"commandID":34,"payloadSize":255,"commandFrame":{}},"linkquality":50,"groupID":1,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(deepClone(events.message[0])).toStrictEqual(expected); await mockAdapterEvents[''] @@ -4139,7 +4144,7 @@ describe('Controller', () => { }); expect(events.deviceJoined.length).toBe(1); - expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[]}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); + expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); expect(events.deviceInterview.length).toBe(1); expect(deepClone(events.deviceInterview[0])).toStrictEqual({"status":"successful","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); expect((controller.getDeviceByIeeeAddr('0x00000000017171f8')).networkAddress).toBe(0x71f8); @@ -4170,7 +4175,7 @@ describe('Controller', () => { }); expect(events.message.length).toBe(1); - const expected = {"type":"commandNotification","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[],"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[],"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []},"data":{"options":0x5488,"srcID":0x017171f8,"frameCounter":4601,"commandID":0x13,"payloadSize":0,"commandFrame":{},"gppNwkAddr": 129,"gppGddLink":0xd8},"linkquality":50,"groupID":0,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"commandNotification","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []},"data":{"options":0x5488,"srcID":0x017171f8,"frameCounter":4601,"commandID":0x13,"payloadSize":0,"commandFrame":{},"gppNwkAddr": 129,"gppGddLink":0xd8},"linkquality":50,"groupID":0,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(deepClone(events.message[0])).toStrictEqual(expected); // Remove green power device from network @@ -4190,7 +4195,7 @@ describe('Controller', () => { expect(controller.getDeviceByIeeeAddr('0x00000000017171f8')).toBeUndefined(); expect(Device.byIeeeAddr('0x00000000017171f8')).toBeUndefined(); - expect(deepClone(Device.byIeeeAddr('0x00000000017171f8', true))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[]}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":false,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":true,"meta":{}}); + expect(deepClone(Device.byIeeeAddr('0x00000000017171f8', true))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":false,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":true,"meta":{}}); // Re-add device await mockAdapterEvents['zclData']({ @@ -4202,7 +4207,7 @@ describe('Controller', () => { groupID: 0, }); - expect(deepClone(Device.byIeeeAddr('0x00000000017171f8'))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[]}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":false,"meta":{}}); + expect(deepClone(Device.byIeeeAddr('0x00000000017171f8'))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":false,"meta":{}}); }); it('Get input/ouptut clusters', async () => { @@ -4267,8 +4272,8 @@ describe('Controller', () => { // We need to send the data after it's been queued, but before we await // the promise. Hijacking queueRequest seems easiest. const origQueueRequest = endpoint.queueRequest; - endpoint.queueRequest = async req => { - const f = origQueueRequest.call(endpoint, req, 'active'); + endpoint.queueRequest = async (req, d) => { + const f = origQueueRequest.call(endpoint, req, d, 'active'); const data = { wasBroadcast: false, @@ -4292,7 +4297,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); const device = controller.getDeviceByIeeeAddr('0x129'); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push({resolve: () => {}, reject: () => {}, func: async () => {}}); + endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockReturnValueOnce(null) const result = endpoint.write('genOnOff', {onOff: 1}, {disableResponse: true, sendWhen: 'active'}); @@ -4308,6 +4313,7 @@ describe('Controller', () => { } await mockAdapterEvents['zclData'](data); + await result; expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1); expect((await result)).toBe(undefined); await mockAdapterEvents['zclData'](data); @@ -4319,7 +4325,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); const device = controller.getDeviceByIeeeAddr('0x129'); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push({resolve: () => {}, reject: () => {}, func: async () => {}}); + endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {throw new Error('Dogs barking too hard')}); const result = endpoint.write('genOnOff', {onOff: 1}, {disableResponse: true, sendWhen: 'active'}); @@ -4356,7 +4362,7 @@ describe('Controller', () => { mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockImplementationOnce(async () => { throw new Error('Dogs barking too hard');}); - endpoint.pendingRequests.push({resolve: () => {}, reject: () => {}, func: async () => {}, expires: Date.now()+100}); + endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); const result = endpoint.write('genOnOff', {onOff: 10}, {disableResponse: true, sendWhen: 'active'}); expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(0); @@ -4389,9 +4395,8 @@ describe('Controller', () => { await device.interview(); mocksendZclFrameToEndpoint.mockClear(); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push({resolve: () => {}, reject: () => {}, func: async () => { - await endpoint.sendPendingRequests(false); - }, sendWhen: "active"}); + endpoint.pendingRequests.push(new Request(async () => { + await endpoint.sendPendingRequests(false);}, [], 100)); const result = endpoint.write('genOnOff', {onOff: 10}, {disableResponse: true, sendWhen: 'active'}); await endpoint.sendPendingRequests(false); await result; @@ -4408,7 +4413,7 @@ describe('Controller', () => { await device.interview(); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push({resolve: () => {}, reject: () => {}, func: async () => {}, sendWhen: 'fastpoll'}); + endpoint.pendingRequests.push(new Request(async () => {}, [], 100, 'fastpoll', undefined, () => {}, () => {})); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockReturnValueOnce(null); @@ -4480,7 +4485,7 @@ describe('Controller', () => { groupID: 171, }); - const expected = {"type":"read","device":{"ID":3,"_applicationVersion":2,"_dateCode":"201901","_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"inputClusters":[],"outputClusters":[],"ID":2,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"inputClusters":[],"outputClusters":[],"ID":3,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"inputClusters":[],"outputClusters":[],"ID":4,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"inputClusters":[],"outputClusters":[],"ID":5,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},{"inputClusters":[],"outputClusters":[],"ID":6,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]}],"_events":{},"_eventsCount":0,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_ieeeAddr":"0x171","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_manufacturerID":1212,"_manufacturerName":"Xioami","_modelID":"lumi.remote.b286opcn01","_networkAddress":171,"_powerSource":"Mains (single phase)","_softwareBuildID":"1.01","_stackVersion":101,"_type":"EndDevice","_zclVersion":1,"_linkquality":19,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}},"endpoint":{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[]},"data":["mainsVoltage",9999],"linkquality":19,"groupID":171,"cluster":"genPowerCfg","meta":{"zclTransactionSequenceNumber":40,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":0,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"read","device":{"ID":3,"_applicationVersion":2,"_dateCode":"201901","_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":2,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":3,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":4,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":5,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":6,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_events":{},"_eventsCount":0,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_ieeeAddr":"0x171","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_manufacturerID":1212,"_manufacturerName":"Xioami","_modelID":"lumi.remote.b286opcn01","_networkAddress":171,"_powerSource":"Mains (single phase)","_softwareBuildID":"1.01","_stackVersion":101,"_type":"EndDevice","_zclVersion":1,"_linkquality":19,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}},"endpoint":{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},"data":["mainsVoltage",9999],"linkquality":19,"groupID":171,"cluster":"genPowerCfg","meta":{"zclTransactionSequenceNumber":40,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":0,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(events.message.length).toBe(1); expect(deepClone(events.message[0])).toStrictEqual(expected); }); From c92abf2572292a9f584cdf73724a85b04b4f4369 Mon Sep 17 00:00:00 2001 From: slugzero Date: Sat, 22 Apr 2023 22:07:54 +0200 Subject: [PATCH 2/4] make pendingRequests a set; add sendPolicies with defaults --- src/controller/model/endpoint.ts | 77 ++++++++++++++++----------- src/controller/tstype.ts | 19 ++++++- test/controller.test.ts | 89 ++++++++++++++++---------------- 3 files changed, 110 insertions(+), 75 deletions(-) diff --git a/src/controller/model/endpoint.ts b/src/controller/model/endpoint.ts index c96a159014..610e8bf99f 100644 --- a/src/controller/model/endpoint.ts +++ b/src/controller/model/endpoint.ts @@ -1,5 +1,5 @@ import Entity from './entity'; -import {KeyValue, SendRequestWhen} from '../tstype'; +import {KeyValue, SendRequestWhen, SendPolicy} from '../tstype'; import * as Zcl from '../../zcl'; import ZclTransactionSequenceNumber from '../helpers/zclTransactionSequenceNumber'; import * as ZclFrameConverter from '../helpers/zclFrameConverter'; @@ -14,6 +14,34 @@ const debug = { error: Debug('zigbee-herdsman:controller:endpoint'), }; +const defaultSendPolicy: {[key: number]: SendPolicy} = { + 0x00: 'keep-payload', // Read Attributes + 0x01: 'immediate', // Read Attributes Response + 0x02: 'keep-command', // Write Attributes + 0x03: 'keep-cmd-undiv', // Write Attributes Undivided + 0x04: 'immediate', // Write Attributes Response + 0x05: 'keep-command', // Write Attributes No Response + 0x06: 'keep-payload', // Configure Reporting + 0x07: 'immediate', // Configure Reporting Response + 0x08: 'keep-payload', // Read Reporting Configuration + 0x09: 'immediate', // Read Reporting Configuration Response + 0x0a: 'keep-payload', // Report attributes + 0x0b: 'immediate', // Default Response + 0x0c: 'keep-payload', // Discover Attributes + 0x0d: 'immediate', // Discover Attributes Response + 0x0e: 'keep-payload', // Read Attributes Structured + 0x0f: 'keep-payload', // Write Attributes Structured + 0x10: 'immediate', // Write Attributes Structured response + 0x11: 'keep-payload', // Discover Commands Received + 0x12: 'immediate', // Discover Commands Received Response + 0x13: 'keep-payload', // Discover Commands Generated + 0x14: 'immediate', // Discover Commands Generated Response + 0x15: 'keep-payload', // Discover Attributes Extended + 0x16: 'immediate', // Discover Attributes Extended Response +}; + +type Mutable = { -readonly [P in keyof T ]: T[P] }; + interface ConfigureReportingItem { attribute: string | number | {ID: number; type: number}; minimumReportInterval: number; @@ -76,17 +104,20 @@ export class Request { private _func: (frame: Zcl.ZclFrame) => Promise; frame: Zcl.ZclFrame; expires: number; + sendPolicy: SendPolicy; sendWhen: SendRequestWhen; private _resolveQueue: Array<(value: Type) => void>; private _rejectQueue: Array <(error: Error) => void>; private _lastError: Error; constructor (func: (frame: Zcl.ZclFrame) => Promise, frame: Zcl.ZclFrame, timeout: number, - sendWhen?: SendRequestWhen, lastError?: Error, resolve?:(value: Type) => void, - reject?: (error: Error) => void) { + sendWhen?: SendRequestWhen, sendPolicy?: SendPolicy, lastError?: Error, + resolve?:(value: Type) => void, reject?: (error: Error) => void) { this._func = func; this.frame = frame; this.sendWhen = sendWhen ?? 'active', this.expires = timeout + Date.now(); + this.sendPolicy = sendPolicy ?? (typeof frame.getCommand !== 'function' ? + undefined : defaultSendPolicy[frame.getCommand().ID]); this._resolveQueue = resolve === undefined ? new Array<(value: Type) => void>() : new Array<(value: Type) => void>(resolve); this._rejectQueue = reject === undefined ? @@ -131,8 +162,7 @@ class Endpoint extends Entity { private _binds: BindInternal[]; private _configuredReportings: ConfiguredReportingInternal[]; public meta: KeyValue; - private pendingRequests: Request[]; - private postponedRequests: Request[]; + private pendingRequests: Set; private sendInProgress: boolean; // Getters/setters @@ -199,8 +229,7 @@ class Endpoint extends Entity { this._binds = binds; this._configuredReportings = configuredReportings; this.meta = meta; - this.pendingRequests = []; - this.postponedRequests = []; + this.pendingRequests = new Set; this.sendInProgress =false; } @@ -309,12 +338,12 @@ class Endpoint extends Entity { } public hasPendingRequests(): boolean { - return this.pendingRequests.length > 0; + return this.pendingRequests.size > 0; } public async sendPendingRequests(fastPolling: boolean): Promise { - if (this.pendingRequests.length === 0) return; + if (this.pendingRequests.size === 0) return; if (this.sendInProgress) { debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): sendPendingRequests already in progress`); @@ -324,25 +353,19 @@ class Endpoint extends Entity { // Remove expired requests first const now = Date.now(); - this.pendingRequests = this.pendingRequests.filter((request) => { - const isExpired = now > request.expires; - if (isExpired) { + for (const request of this.pendingRequests) { + if (now > request.expires) { debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): discard after timeout. ` + - `Size before: ${this.pendingRequests.length}`); + `Size before: ${this.pendingRequests.size}`); request.reject(); + this.pendingRequests.delete(request); } - return !isExpired; - }); + } - this.postponedRequests = []; debug.info(`Request Queue (${this.deviceIeeeAddress}/${this.ID}): send pending requests (` + - `${this.pendingRequests.length}, ${fastPolling})`); - - // Elements can be added to the queue while send is in progress. - // Using a while loop ensures that newly added elements are also sent in the end - while (this.pendingRequests.length > 0) { - const request = this.pendingRequests.shift(); + `${this.pendingRequests.size}, ${fastPolling})`); + for (const request of this.pendingRequests) { if ((fastPolling && request.sendWhen == 'fastpoll') || request.sendWhen == 'active') { try { const result = await request.send(); @@ -353,13 +376,9 @@ class Endpoint extends Entity { `${(request.expires - now) / 1000} seconds`); request.reject(error); } - } - else { - this.postponedRequests.push(request); + this.pendingRequests.delete(request); } } - this.pendingRequests = this.postponedRequests; - this.postponedRequests = []; this.sendInProgress = false; } @@ -368,7 +387,7 @@ class Endpoint extends Entity { `Timeout ${this.getDevice().pendingRequestTimeout/1000} seconds`); return new Promise((resolve, reject): void => { request.addCallbacks(resolve, reject); - this.pendingRequests.push(request); + this.pendingRequests.add(request); }); } @@ -398,7 +417,7 @@ class Endpoint extends Entity { // If we already have something queued, we queue directly to avoid // messing up the ordering too much. if (this.hasPendingRequests() || this.sendInProgress) { - debug.info(logPrefix + `queue request (${this.pendingRequests.length} / ${this.sendInProgress})))`); + debug.info(logPrefix + `queue request (${this.pendingRequests.size} / ${this.sendInProgress})))`); return this.queueRequest(request); } diff --git a/src/controller/tstype.ts b/src/controller/tstype.ts index 813b5e8063..7b00673abe 100644 --- a/src/controller/tstype.ts +++ b/src/controller/tstype.ts @@ -1,6 +1,23 @@ // eslint-disable-next-line interface KeyValue {[s: string]: any}; +/* Send request policies: +'bulk': Message must be sent together with other messages in the correct sequence. + No immediate delivery required. +'queue': Request shall be sent 'as-is' as soon as possible. + Multiple identical requests shall be delivered multiple times. + Not strict ordering required. +'immediate': Request shall be sent immediately and not be kept for later retries (e.g. response message). +'keep-payload': Request shall be sent as soon as possible. + If immediate delivery fails, the exact same payload is only sent once, even if there were + multiple requests. +'keep-command': Request shall be sent as soon as possible. + If immediate delivery fails, only the latest command for each command ID is kept for delivery. +'keep-cmd-undiv': Request shall be sent as soon as possible. + If immediate delivery fails, only the latest undivided set of commands is sent for each unique + set of command IDs. +*/ +type SendPolicy = 'bulk' | 'queue' | 'immediate' | 'keep-payload' | 'keep-command' | 'keep-cmd-undiv'; type SendRequestWhen = 'immediate' | 'fastpoll' | 'active'; type DeviceType = 'Coordinator' | 'Router' | 'EndDevice' | 'Unknown' | 'GreenPower'; @@ -25,5 +42,5 @@ interface GreenPowerDeviceJoinedPayload { export { KeyValue, DatabaseEntry, EntityType, DeviceType, GreenPowerEvents, GreenPowerDeviceJoinedPayload, - SendRequestWhen + SendRequestWhen, SendPolicy }; \ No newline at end of file diff --git a/test/controller.test.ts b/test/controller.test.ts index e934b7abbe..bc13b45818 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -585,8 +585,7 @@ describe('Controller', () => { "_events":{},"_eventsCount":0, inputClusters: [10], outputClusters: [11], - pendingRequests: [], - postponedRequests: [], + pendingRequests: new Object, sendInProgress: false, profileID: 2, ID: 1, @@ -600,8 +599,7 @@ describe('Controller', () => { "_events":{},"_eventsCount":0, inputClusters: [1], outputClusters: [0], - pendingRequests: [], - postponedRequests: [], + pendingRequests: new Object, sendInProgress: false, profileID: 3, meta: {}, @@ -802,7 +800,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); expect(equalsPartial(events.deviceJoined[0].device, {ID: 2, networkAddress: 129, ieeeAddr: '0x129'})).toBeTruthy(); expect(events.deviceInterview[0]).toStrictEqual({"device":{"_events":{},"_eventsCount":0,"meta": {}, "_skipDefaultResponse": false, "_skipTimeResponse": false, "_lastSeen": deepClone(Date.now()), "ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[],"_type":"Unknown","_ieeeAddr":"0x129","_interviewCompleted":false,"_interviewing":false,"_networkAddress":129},"status":"started"}); - const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"outputClusters":[2],"pendingRequests": [], "postponedRequests": [], "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"meta":{},"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; + const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"outputClusters":[2],"pendingRequests": new Object, "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"meta":{},"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; expect(events.deviceInterview[1]).toStrictEqual({"status":"successful","device":device}); expect(deepClone(controller.getDeviceByNetworkAddress(129))).toStrictEqual(device); expect(events.deviceInterview.length).toBe(2); @@ -820,7 +818,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); expect(equalsPartial(events.deviceJoined[0].device, {ID: 2, networkAddress: 129, ieeeAddr: '0x129'})).toBeTruthy(); expect(events.deviceInterview[0]).toStrictEqual({"device":{"meta": {}, "_skipDefaultResponse": false,"_events":{},"_eventsCount":0,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()), "ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[],"_ieeeAddr":"0x129","_interviewCompleted":false,"_interviewing":false,"_networkAddress":129,"_type":"Unknown"},"status":"started"}); - const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"meta":{},"outputClusters":[2],"pendingRequests": [], "postponedRequests": [], "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; + const device = {"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": deepClone(Date.now()),"_type":"Unknown","_ieeeAddr":"0x129","_networkAddress":129,"meta": {},"_endpoints":[{"_events":{},"_eventsCount":0,"clusters": {}, "ID":1,"inputClusters":[0,1],"meta":{},"outputClusters":[2],"pendingRequests": new Object, "sendInProgress": false,"deviceNetworkAddress":129,"deviceIeeeAddress":"0x129","_binds": [], "_configuredReportings": [],"deviceID":5,"profileID":99}],"_type":"Router","_manufacturerID":1212,"_manufacturerName":"KoenAndCo","_powerSource":"Mains (single phase)","_modelID":"myModelID","_applicationVersion":2,"_stackVersion":101,"_zclVersion":1,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_dateCode":"201901","_softwareBuildID":"1.01","_interviewCompleted":true,"_interviewing":false}; expect(events.deviceInterview[1]).toStrictEqual({"status":"successful","device":device}); expect(deepClone(controller.getDeviceByIeeeAddr('0x129'))).toStrictEqual(device); expect(events.deviceInterview.length).toBe(2); @@ -1240,7 +1238,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "_binds": [], "_configuredReportings": [], "meta":{}, @@ -1278,7 +1276,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0,1], "outputClusters":[2], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1356,7 +1354,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1388,7 +1386,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1447,7 +1445,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1480,7 +1478,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1553,7 +1551,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1578,7 +1576,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1611,7 +1609,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "meta":{}, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", @@ -1684,7 +1682,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1721,7 +1719,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -1979,6 +1977,7 @@ describe('Controller', () => { it('Xiaomi WXCJKG11LM join (get simple descriptor for endpoint 2 fails)', async () => { // https://github.com/koenkk/zigbee2mqtt/issues/2844 + Date.now.mockReturnValue(150); await controller.start(); await mockAdapterEvents['deviceJoined']({networkAddress: 171, ieeeAddr: '0x171'}); expect(events.deviceInterview.length).toBe(2); @@ -2037,7 +2036,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":150, "deviceIeeeAddress":"0x150", "_binds": [], @@ -2094,7 +2093,7 @@ describe('Controller', () => { "outputClusters":[ ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":151, "deviceIeeeAddress":"0x151", "_binds": [], @@ -2171,7 +2170,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -2204,7 +2203,7 @@ describe('Controller', () => { "deviceID": 5, "inputClusters":[0, 1], "outputClusters":[2], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "deviceNetworkAddress":129, "deviceIeeeAddress":"0x129", "_binds": [], @@ -2984,7 +2983,7 @@ describe('Controller', () => { const line = JSON.stringify({"id":3,"type":"EndDevice","ieeeAddr":"0x90fd9ffffe4b64ae","nwkAddr":19468,"manufId":4476,"manufName":"IKEA of Sweden","powerSource":"Battery","modelId":"TRADFRI remote control","epList":[1],"endpoints":{"1":{"profId":49246,"epId":1,"devId":2096,"inClusterList":[0,1,3,9,2821,4096],"outClusterList":[3,4,5,6,8,25,4096],"clusters":{}}},"appVersion":17,"stackVersion":87,"hwVersion":1,"dateCode":"20170302","swBuildId":"1.2.214","zclVersion":1,"interviewCompleted":true,"_id":"fJ5pmjqKRYbNvslK"}); fs.writeFileSync(options.databasePath, line + "\n"); await controller.start(); - const expected = {"ID": 3, "_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170302", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"clusters": {}, "ID": 1, "deviceID": 2096, "_binds": [], "_configuredReportings": [],"deviceIeeeAddress": "0x90fd9ffffe4b64ae", "deviceNetworkAddress": 19468, "inputClusters": [0, 1, 3, 9, 2821, 4096], "outputClusters": [3, 4, 5, 6, 8, 25, 4096], "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x90fd9ffffe4b64ae", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {}, "_modelID": "TRADFRI remote control", "_networkAddress": 19468, "_powerSource": "Battery", "_softwareBuildID": "1.2.214", "_stackVersion": 87, "_type": "EndDevice", "_zclVersion": 1} + const expected = {"ID": 3, "_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170302", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"clusters": {}, "ID": 1, "deviceID": 2096, "_binds": [], "_configuredReportings": [],"deviceIeeeAddress": "0x90fd9ffffe4b64ae", "deviceNetworkAddress": 19468, "inputClusters": [0, 1, 3, 9, 2821, 4096], "outputClusters": [3, 4, 5, 6, 8, 25, 4096], "pendingRequests": new Object, "sendInProgress": false, "profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x90fd9ffffe4b64ae", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {}, "_modelID": "TRADFRI remote control", "_networkAddress": 19468, "_powerSource": "Battery", "_softwareBuildID": "1.2.214", "_stackVersion": 87, "_type": "EndDevice", "_zclVersion": 1} expect(deepClone(controller.getDeviceByIeeeAddr("0x90fd9ffffe4b64ae"))).toStrictEqual(expected); }); @@ -3305,10 +3304,10 @@ describe('Controller', () => { fs.writeFileSync(options.databasePath, database); await controller.start(); expect((controller.getDevices()).length).toBe(4); - expect(deepClone(controller.getDeviceByIeeeAddr('0x123'))).toStrictEqual({"ID":1,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"_events":{},"_eventsCount":0,"inputClusters":[],"outputClusters":[],"profileID":260,"ID":1,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":257,"ID":2,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":261,"ID":3,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":263,"ID":4,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":264,"ID":5,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":265,"ID":6,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":1024,"inputClusters":[],"outputClusters":[1280],"profileID":260,"ID":11,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x123","_interviewCompleted":false,"_interviewing":false,"_lastSeen":null,"_manufacturerID":0,"_networkAddress":0,"_type":"Coordinator","_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x000b57fffec6a5b2'))).toStrictEqual({"ID": 3,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170331", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests":[],"postponedRequests":[],"sendInProgress":false,"profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x000b57fffec6a5b2", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {"reporting": 1}, "_modelID": "TRADFRI bulb E27 WS opal 980lm", "_networkAddress": 40369, "_powerSource": "Mains (single phase)", "_softwareBuildID": "1.2.217", "_stackVersion": 87, "_type": "Router", "_zclVersion": 1}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45517'))).toStrictEqual({"ID":4,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{"genBasic":{"dir":{"value":3},"attributes":{"modelId":"RWL021"}}},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[{"type":"endpoint","endpointID":1,"deviceIeeeAddr":"0x000b57fffec6a5b2"}],"_configuredReportings":[{"cluster":1,"attrId":0,"minRepIntval":1,"maxRepIntval":20,"repChange":2}],"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45517","_interviewCompleted":true,"_interviewing":false,"_lastSeen":123,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6538,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); - expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45518'))).toStrictEqual({"ID":6,"_checkinInterval":123456,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":123456000,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,32,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45518","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6536,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x123'))).toStrictEqual({"ID":1,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"_events":{},"_eventsCount":0,"inputClusters":[],"outputClusters":[],"profileID":260,"ID":1,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":257,"ID":2,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":261,"ID":3,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":263,"ID":4,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":264,"ID":5,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":5,"inputClusters":[],"outputClusters":[],"profileID":265,"ID":6,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":1024,"inputClusters":[],"outputClusters":[1280],"profileID":260,"ID":11,"clusters":{},"deviceIeeeAddress":"0x123","deviceNetworkAddress":0,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false}],"_ieeeAddr":"0x123","_interviewCompleted":false,"_interviewing":false,"_lastSeen":null,"_manufacturerID":0,"_networkAddress":0,"_type":"Coordinator","_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x000b57fffec6a5b2'))).toStrictEqual({"ID": 3,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_lastSeen": null, "_applicationVersion": 17, "_dateCode": "20170331", "_endpoints": [{"_events":{},"_eventsCount":0,"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests":new Object,"sendInProgress":false,"profileID": 49246}], "_hardwareVersion": 1, "_ieeeAddr": "0x000b57fffec6a5b2", "_interviewCompleted": true,"_events":{},"_eventsCount":0, "_interviewing": false, "_manufacturerID": 4476, "_manufacturerName": "IKEA of Sweden", "meta": {"reporting": 1}, "_modelID": "TRADFRI bulb E27 WS opal 980lm", "_networkAddress": 40369, "_powerSource": "Mains (single phase)", "_softwareBuildID": "1.2.217", "_stackVersion": 87, "_type": "Router", "_zclVersion": 1}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45517'))).toStrictEqual({"ID":4,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{"genBasic":{"dir":{"value":3},"attributes":{"modelId":"RWL021"}}},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[{"type":"endpoint","endpointID":1,"deviceIeeeAddr":"0x000b57fffec6a5b2"}],"_configuredReportings":[{"cluster":1,"attrId":0,"minRepIntval":1,"maxRepIntval":20,"repChange":2}],"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45517","deviceNetworkAddress":6538,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45517","_interviewCompleted":true,"_interviewing":false,"_lastSeen":123,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6538,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); + expect(deepClone(controller.getDeviceByIeeeAddr('0x0017880104e45518'))).toStrictEqual({"ID":6,"_checkinInterval":123456,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":123456000,"_defaultSendRequestWhen": "immediate","_applicationVersion":2,"_dateCode":"20160302","_endpoints":[{"deviceID":2096,"_events":{},"_eventsCount":0,"inputClusters":[0],"outputClusters":[0,3,4,6,8,5],"profileID":49246,"ID":1,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"deviceID":12,"inputClusters":[0,1,3,15,32,64512],"outputClusters":[25],"profileID":260,"ID":2,"clusters":{},"deviceIeeeAddress":"0x0017880104e45518","deviceNetworkAddress":6536,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false}],"_hardwareVersion":1,"_ieeeAddr":"0x0017880104e45518","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":4107,"_manufacturerName":"Philips","_modelID":"RWL021","_networkAddress":6536,"_powerSource":"Battery","_softwareBuildID":"5.45.1.17846","_stackVersion":1,"_type":"EndDevice","_zclVersion":1,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{"configured":1}}); expect((await controller.getGroups({})).length).toBe(2); const group1 = controller.getGroupByID(1); @@ -3316,7 +3315,7 @@ describe('Controller', () => { expect(deepClone(group1)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 2, "groupID": 1, "_members": [], "meta": {}}); const group2 = controller.getGroupByID(2); group2._members = Array.from(group2._members); - expect(deepClone(group2)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 5, "groupID": 2, "_members": [{"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "_events":{},"_eventsCount":0,"deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, "profileID": 49246}], "meta": {}}); + expect(deepClone(group2)).toStrictEqual({"_events":{},"_eventsCount":0,"databaseID": 5, "groupID": 2, "_members": [{"meta":{},"_binds": [], "_configuredReportings": [], "clusters": {}, "ID": 1, "_events":{},"_eventsCount":0,"deviceID": 544, "deviceIeeeAddress": "0x000b57fffec6a5b2", "deviceNetworkAddress": 40369, "inputClusters": [0, 3, 4, 5, 6, 8, 768, 2821, 4096], "outputClusters": [5, 25, 32, 4096], "pendingRequests": new Object, "sendInProgress": false, "profileID": 49246}], "meta": {}}); }); it('Shouldnt load device from group databaseentry', async () => { @@ -3466,7 +3465,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3508,7 +3507,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3577,7 +3576,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3620,7 +3619,7 @@ describe('Controller', () => { "outputClusters":[ 2 ], - "pendingRequests": [], "postponedRequests": [], "sendInProgress": false, + "pendingRequests": new Object, "sendInProgress": false, "profileID":99, "ID":1, "clusters":{ @@ -3895,7 +3894,7 @@ describe('Controller', () => { }); expect(events.deviceJoined.length).toBe(1); - expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"inputClusters":[],"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": [],"_events":{},"_eventsCount":0,"meta":{}}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); + expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"inputClusters":[],"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false,"ID":242,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": [],"_events":{},"_eventsCount":0,"meta":{}}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); expect(events.deviceInterview.length).toBe(1); expect(deepClone(events.deviceInterview[0])).toStrictEqual({"status":"successful","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}}}); expect((controller.getDeviceByIeeeAddr('0x000000000046f4fe')).networkAddress).toBe(0xf4fe); @@ -3924,7 +3923,7 @@ describe('Controller', () => { }); expect(events.message.length).toBe(1); - const expected = {"type":"commandNotification","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []}],"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []},"data":{"options":0,"srcID":0x46f4fe,"frameCounter":228,"commandID":34,"payloadSize":255,"commandFrame":{}},"linkquality":50,"groupID":1,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"commandNotification","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []}],"_ieeeAddr":"0x000000000046f4fe","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0xf4fe,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x000000000046f4fe","deviceNetworkAddress":0xf4fe,"_binds":[], "_configuredReportings": []},"data":{"options":0,"srcID":0x46f4fe,"frameCounter":228,"commandID":34,"payloadSize":255,"commandFrame":{}},"linkquality":50,"groupID":1,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(deepClone(events.message[0])).toStrictEqual(expected); await mockAdapterEvents[''] @@ -4144,7 +4143,7 @@ describe('Controller', () => { }); expect(events.deviceJoined.length).toBe(1); - expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); + expect(deepClone(events.deviceJoined[0])).toStrictEqual({"device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); expect(events.deviceInterview.length).toBe(1); expect(deepClone(events.deviceInterview[0])).toStrictEqual({"status":"successful","device":{"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":null,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}}}); expect((controller.getDeviceByIeeeAddr('0x00000000017171f8')).networkAddress).toBe(0x71f8); @@ -4175,7 +4174,7 @@ describe('Controller', () => { }); expect(events.message.length).toBe(1); - const expected = {"type":"commandNotification","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []},"data":{"options":0x5488,"srcID":0x017171f8,"frameCounter":4601,"commandID":0x13,"payloadSize":0,"commandFrame":{},"gppNwkAddr": 129,"gppGddLink":0xd8},"linkquality":50,"groupID":0,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"commandNotification","device":{"ID":2,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []}],"_events":{},"_eventsCount":0,"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality": 50,"_manufacturerID":null,"_skipDefaultResponse": false,"_skipTimeResponse":false,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","meta":{}},"endpoint":{"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false,"ID":242,"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"_binds":[], "_configuredReportings": []},"data":{"options":0x5488,"srcID":0x017171f8,"frameCounter":4601,"commandID":0x13,"payloadSize":0,"commandFrame":{},"gppNwkAddr": 129,"gppGddLink":0xd8},"linkquality":50,"groupID":0,"cluster":"greenPower","meta":{"zclTransactionSequenceNumber":10,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":1,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(deepClone(events.message[0])).toStrictEqual(expected); // Remove green power device from network @@ -4195,7 +4194,7 @@ describe('Controller', () => { expect(controller.getDeviceByIeeeAddr('0x00000000017171f8')).toBeUndefined(); expect(Device.byIeeeAddr('0x00000000017171f8')).toBeUndefined(); - expect(deepClone(Device.byIeeeAddr('0x00000000017171f8', true))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":false,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":true,"meta":{}}); + expect(deepClone(Device.byIeeeAddr('0x00000000017171f8', true))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":false,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":true,"meta":{}}); // Re-add device await mockAdapterEvents['zclData']({ @@ -4207,7 +4206,7 @@ describe('Controller', () => { groupID: 0, }); - expect(deepClone(Device.byIeeeAddr('0x00000000017171f8'))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":false,"meta":{}}); + expect(deepClone(Device.byIeeeAddr('0x00000000017171f8'))).toStrictEqual({"ID":2,"_events":{},"_eventsCount":0,"_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_skipDefaultResponse": false,"_skipTimeResponse":false,"_endpoints":[{"ID":242,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"clusters":{},"deviceIeeeAddress":"0x00000000017171f8","deviceNetworkAddress":0x71f8,"inputClusters":[],"meta":{},"outputClusters":[],"pendingRequests":new Object, "sendInProgress": false}],"_ieeeAddr":"0x00000000017171f8","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_linkquality":50,"_manufacturerID":null,"_modelID":"GreenPower_2","_networkAddress":0x71f8,"_type":"GreenPower","_deleted":false,"meta":{}}); }); it('Get input/ouptut clusters', async () => { @@ -4297,7 +4296,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); const device = controller.getDeviceByIeeeAddr('0x129'); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); + endpoint.pendingRequests.add(new Request(async () => {}, [], 100)); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockReturnValueOnce(null) const result = endpoint.write('genOnOff', {onOff: 1}, {disableResponse: true, sendWhen: 'active'}); @@ -4325,7 +4324,7 @@ describe('Controller', () => { await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'}); const device = controller.getDeviceByIeeeAddr('0x129'); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); + endpoint.pendingRequests.add(new Request(async () => {}, [], 100)); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {throw new Error('Dogs barking too hard')}); const result = endpoint.write('genOnOff', {onOff: 1}, {disableResponse: true, sendWhen: 'active'}); @@ -4362,7 +4361,7 @@ describe('Controller', () => { mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockImplementationOnce(async () => { throw new Error('Dogs barking too hard');}); - endpoint.pendingRequests.push(new Request(async () => {}, [], 100)); + endpoint.pendingRequests.add(new Request(async () => {}, [], 100)); const result = endpoint.write('genOnOff', {onOff: 10}, {disableResponse: true, sendWhen: 'active'}); expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(0); @@ -4384,7 +4383,7 @@ describe('Controller', () => { error = e; } expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(0); - expect(endpoint.pendingRequests.length).toBe(0); + expect(endpoint.pendingRequests.size).toBe(0); Date.now.mockReturnValue(150); }); @@ -4395,13 +4394,13 @@ describe('Controller', () => { await device.interview(); mocksendZclFrameToEndpoint.mockClear(); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push(new Request(async () => { + endpoint.pendingRequests.add(new Request(async () => { await endpoint.sendPendingRequests(false);}, [], 100)); const result = endpoint.write('genOnOff', {onOff: 10}, {disableResponse: true, sendWhen: 'active'}); await endpoint.sendPendingRequests(false); await result; expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1); - expect(endpoint.pendingRequests.length).toBe(0); + expect(endpoint.pendingRequests.size).toBe(0); }); it('Fast polling', async () => { @@ -4413,7 +4412,7 @@ describe('Controller', () => { await device.interview(); const endpoint = device.getEndpoint(1); - endpoint.pendingRequests.push(new Request(async () => {}, [], 100, 'fastpoll', undefined, () => {}, () => {})); + endpoint.pendingRequests.add(new Request(async () => {}, [], 100, 'fastpoll', undefined, undefined, () => {}, () => {})); mocksendZclFrameToEndpoint.mockClear(); mocksendZclFrameToEndpoint.mockReturnValueOnce(null); @@ -4485,7 +4484,7 @@ describe('Controller', () => { groupID: 171, }); - const expected = {"type":"read","device":{"ID":3,"_applicationVersion":2,"_dateCode":"201901","_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":2,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":3,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":4,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":5,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":6,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false}],"_events":{},"_eventsCount":0,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_ieeeAddr":"0x171","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_manufacturerID":1212,"_manufacturerName":"Xioami","_modelID":"lumi.remote.b286opcn01","_networkAddress":171,"_powerSource":"Mains (single phase)","_softwareBuildID":"1.01","_stackVersion":101,"_type":"EndDevice","_zclVersion":1,"_linkquality":19,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}},"endpoint":{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":[], "postponedRequests":[], "sendInProgress": false},"data":["mainsVoltage",9999],"linkquality":19,"groupID":171,"cluster":"genPowerCfg","meta":{"zclTransactionSequenceNumber":40,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":0,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; + const expected = {"type":"read","device":{"ID":3,"_applicationVersion":2,"_dateCode":"201901","_pendingRequestTimeout":0,"_defaultSendRequestWhen": "immediate","_endpoints":[{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":2,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":3,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":4,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":5,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},{"inputClusters":[],"outputClusters":[],"ID":6,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false}],"_events":{},"_eventsCount":0,"_hardwareVersion":3,"_events":{},"_eventsCount":0,"_ieeeAddr":"0x171","_interviewCompleted":true,"_interviewing":false,"_lastSeen":150,"_manufacturerID":1212,"_manufacturerName":"Xioami","_modelID":"lumi.remote.b286opcn01","_networkAddress":171,"_powerSource":"Mains (single phase)","_softwareBuildID":"1.01","_stackVersion":101,"_type":"EndDevice","_zclVersion":1,"_linkquality":19,"_skipDefaultResponse":false,"_skipTimeResponse":false,"meta":{}},"endpoint":{"deviceID":5,"inputClusters":[0,1,2],"outputClusters":[2],"profileID":99,"ID":1,"clusters":{},"deviceIeeeAddress":"0x171","deviceNetworkAddress":171,"_binds":[],"_configuredReportings":[],"_events":{},"_eventsCount":0,"meta":{},"pendingRequests":new Object, "sendInProgress": false},"data":["mainsVoltage",9999],"linkquality":19,"groupID":171,"cluster":"genPowerCfg","meta":{"zclTransactionSequenceNumber":40,"manufacturerCode":null,"frameControl":{"reservedBits":0,"frameType":0,"direction":0,"disableDefaultResponse":true,"manufacturerSpecific":false}}}; expect(events.message.length).toBe(1); expect(deepClone(events.message[0])).toStrictEqual(expected); }); From 297c2b43bb92fafe5cdc6e52195b6d5eb29d0fc6 Mon Sep 17 00:00:00 2001 From: slugzero Date: Mon, 24 Apr 2023 22:55:49 +0200 Subject: [PATCH 3/4] Remove unused "Mutable" type --- src/controller/model/endpoint.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controller/model/endpoint.ts b/src/controller/model/endpoint.ts index 610e8bf99f..5d448743f8 100644 --- a/src/controller/model/endpoint.ts +++ b/src/controller/model/endpoint.ts @@ -40,8 +40,6 @@ const defaultSendPolicy: {[key: number]: SendPolicy} = { 0x16: 'immediate', // Discover Attributes Extended Response }; -type Mutable = { -readonly [P in keyof T ]: T[P] }; - interface ConfigureReportingItem { attribute: string | number | {ID: number; type: number}; minimumReportInterval: number; From cfdfe3d68ceb6662cee6ee4c645cc86c38eb4294 Mon Sep 17 00:00:00 2001 From: slugzero Date: Tue, 25 Apr 2023 22:09:38 +0200 Subject: [PATCH 4/4] move Request to helpers/request.ts --- src/controller/helpers/request.ts | 82 +++++++++++++++++++++++++++++++ src/controller/model/endpoint.ts | 81 +----------------------------- test/controller.test.ts | 2 +- 3 files changed, 85 insertions(+), 80 deletions(-) create mode 100644 src/controller/helpers/request.ts diff --git a/src/controller/helpers/request.ts b/src/controller/helpers/request.ts new file mode 100644 index 0000000000..21de408c47 --- /dev/null +++ b/src/controller/helpers/request.ts @@ -0,0 +1,82 @@ +import {SendRequestWhen, SendPolicy} from '../tstype'; +import * as Zcl from '../../zcl'; + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ +class Request { + + static defaultSendPolicy: {[key: number]: SendPolicy} = { + 0x00: 'keep-payload', // Read Attributes + 0x01: 'immediate', // Read Attributes Response + 0x02: 'keep-command', // Write Attributes + 0x03: 'keep-cmd-undiv', // Write Attributes Undivided + 0x04: 'immediate', // Write Attributes Response + 0x05: 'keep-command', // Write Attributes No Response + 0x06: 'keep-payload', // Configure Reporting + 0x07: 'immediate', // Configure Reporting Response + 0x08: 'keep-payload', // Read Reporting Configuration + 0x09: 'immediate', // Read Reporting Configuration Response + 0x0a: 'keep-payload', // Report attributes + 0x0b: 'immediate', // Default Response + 0x0c: 'keep-payload', // Discover Attributes + 0x0d: 'immediate', // Discover Attributes Response + 0x0e: 'keep-payload', // Read Attributes Structured + 0x0f: 'keep-payload', // Write Attributes Structured + 0x10: 'immediate', // Write Attributes Structured response + 0x11: 'keep-payload', // Discover Commands Received + 0x12: 'immediate', // Discover Commands Received Response + 0x13: 'keep-payload', // Discover Commands Generated + 0x14: 'immediate', // Discover Commands Generated Response + 0x15: 'keep-payload', // Discover Attributes Extended + 0x16: 'immediate', // Discover Attributes Extended Response + }; + + private _func: (frame: Zcl.ZclFrame) => Promise; + frame: Zcl.ZclFrame; + expires: number; + sendPolicy: SendPolicy; + sendWhen: SendRequestWhen; + private _resolveQueue: Array<(value: Type) => void>; + private _rejectQueue: Array <(error: Error) => void>; + private _lastError: Error; + constructor (func: (frame: Zcl.ZclFrame) => Promise, frame: Zcl.ZclFrame, timeout: number, + sendWhen?: SendRequestWhen, sendPolicy?: SendPolicy, lastError?: Error, + resolve?:(value: Type) => void, reject?: (error: Error) => void) { + this._func = func; + this.frame = frame; + this.sendWhen = sendWhen ?? 'active', + this.expires = timeout + Date.now(); + this.sendPolicy = sendPolicy ?? (typeof frame.getCommand !== 'function' ? + undefined : Request.defaultSendPolicy[frame.getCommand().ID]); + this._resolveQueue = resolve === undefined ? + new Array<(value: Type) => void>() : new Array<(value: Type) => void>(resolve); + this._rejectQueue = reject === undefined ? + new Array<(error: Error) => void>() : new Array<(error: Error) => void>(reject); + this._lastError = lastError ?? Error("Request rejected before first send"); + } + + addCallbacks(resolve: (value: Type) => void, reject: (error: Error) => void): void { + this._resolveQueue.push(resolve); + this._rejectQueue.push(reject); + } + + reject(error?: Error): void { + this._rejectQueue.forEach(el => el(error ?? this._lastError)); + this._rejectQueue.length = 0; + } + + resolve(value: Type): void { + this._resolveQueue.forEach(el => el(value)); + this._resolveQueue.length = 0; + } + + async send(): Promise { + try { + return await this._func(this.frame); + } catch (error) { + this._lastError = error; + throw (error); + } + } +} + +export default Request; \ No newline at end of file diff --git a/src/controller/model/endpoint.ts b/src/controller/model/endpoint.ts index 5d448743f8..3cab1ddfe0 100644 --- a/src/controller/model/endpoint.ts +++ b/src/controller/model/endpoint.ts @@ -1,8 +1,9 @@ import Entity from './entity'; -import {KeyValue, SendRequestWhen, SendPolicy} from '../tstype'; +import {KeyValue, SendRequestWhen} from '../tstype'; import * as Zcl from '../../zcl'; import ZclTransactionSequenceNumber from '../helpers/zclTransactionSequenceNumber'; import * as ZclFrameConverter from '../helpers/zclFrameConverter'; +import Request from '../helpers/request'; import {Events as AdapterEvents} from '../../adapter'; import Group from './group'; import Device from './device'; @@ -14,32 +15,6 @@ const debug = { error: Debug('zigbee-herdsman:controller:endpoint'), }; -const defaultSendPolicy: {[key: number]: SendPolicy} = { - 0x00: 'keep-payload', // Read Attributes - 0x01: 'immediate', // Read Attributes Response - 0x02: 'keep-command', // Write Attributes - 0x03: 'keep-cmd-undiv', // Write Attributes Undivided - 0x04: 'immediate', // Write Attributes Response - 0x05: 'keep-command', // Write Attributes No Response - 0x06: 'keep-payload', // Configure Reporting - 0x07: 'immediate', // Configure Reporting Response - 0x08: 'keep-payload', // Read Reporting Configuration - 0x09: 'immediate', // Read Reporting Configuration Response - 0x0a: 'keep-payload', // Report attributes - 0x0b: 'immediate', // Default Response - 0x0c: 'keep-payload', // Discover Attributes - 0x0d: 'immediate', // Discover Attributes Response - 0x0e: 'keep-payload', // Read Attributes Structured - 0x0f: 'keep-payload', // Write Attributes Structured - 0x10: 'immediate', // Write Attributes Structured response - 0x11: 'keep-payload', // Discover Commands Received - 0x12: 'immediate', // Discover Commands Received Response - 0x13: 'keep-payload', // Discover Commands Generated - 0x14: 'immediate', // Discover Commands Generated Response - 0x15: 'keep-payload', // Discover Attributes Extended - 0x16: 'immediate', // Discover Attributes Extended Response -}; - interface ConfigureReportingItem { attribute: string | number | {ID: number; type: number}; minimumReportInterval: number; @@ -96,58 +71,6 @@ interface ConfiguredReporting { reportableChange: number, } -/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/ -export class Request { - - private _func: (frame: Zcl.ZclFrame) => Promise; - frame: Zcl.ZclFrame; - expires: number; - sendPolicy: SendPolicy; - sendWhen: SendRequestWhen; - private _resolveQueue: Array<(value: Type) => void>; - private _rejectQueue: Array <(error: Error) => void>; - private _lastError: Error; - constructor (func: (frame: Zcl.ZclFrame) => Promise, frame: Zcl.ZclFrame, timeout: number, - sendWhen?: SendRequestWhen, sendPolicy?: SendPolicy, lastError?: Error, - resolve?:(value: Type) => void, reject?: (error: Error) => void) { - this._func = func; - this.frame = frame; - this.sendWhen = sendWhen ?? 'active', - this.expires = timeout + Date.now(); - this.sendPolicy = sendPolicy ?? (typeof frame.getCommand !== 'function' ? - undefined : defaultSendPolicy[frame.getCommand().ID]); - this._resolveQueue = resolve === undefined ? - new Array<(value: Type) => void>() : new Array<(value: Type) => void>(resolve); - this._rejectQueue = reject === undefined ? - new Array<(error: Error) => void>() : new Array<(error: Error) => void>(reject); - this._lastError = lastError ?? Error("Request rejected before first send"); - } - - addCallbacks(resolve: (value: Type) => void, reject: (error: Error) => void): void { - this._resolveQueue.push(resolve); - this._rejectQueue.push(reject); - } - - reject(error?: Error): void { - this._rejectQueue.forEach(el => el(error ?? this._lastError)); - this._rejectQueue.length = 0; - } - - resolve(value: Type): void { - this._resolveQueue.forEach(el => el(value)); - this._resolveQueue.length = 0; - } - - async send(): Promise { - try { - return await this._func(this.frame); - } catch (error) { - this._lastError = error; - throw (error); - } - } -} - class Endpoint extends Entity { public deviceID?: number; public inputClusters: number[]; diff --git a/test/controller.test.ts b/test/controller.test.ts index bc13b45818..ea3d63845f 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -7,9 +7,9 @@ import equals from 'fast-deep-equal/es6'; import fs from 'fs'; import { ZclFrame } from "../src/zcl"; import { Device, Group} from "../src/controller/model"; -import {Request} from "../src/controller/model/endpoint"; import * as Zcl from '../src/zcl'; import zclTransactionSequenceNumber from '../src/controller/helpers/zclTransactionSequenceNumber'; +import Request from '../src/controller/helpers/request'; import {Adapter} from '../src/adapter'; import path from 'path'; import {Wait} from '../src/utils';