@willieee802/zigbee-herdsman 0.49.4 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +0 -3
- package/.github/workflows/ci.yml +1 -2
- package/.github/workflows/release-please.yml +1 -1
- package/.github/workflows/typedoc.yaml +3 -3
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +143 -0
- package/biome.json +1 -1
- package/dist/adapter/adapter.d.ts +14 -1
- package/dist/adapter/adapter.d.ts.map +1 -1
- package/dist/adapter/adapter.js +17 -0
- package/dist/adapter/adapter.js.map +1 -1
- package/dist/adapter/adapterDiscovery.d.ts.map +1 -1
- package/dist/adapter/adapterDiscovery.js.map +1 -1
- package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +1 -3
- package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
- package/dist/adapter/deconz/adapter/deconzAdapter.js +14 -29
- package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
- package/dist/adapter/deconz/driver/constants.d.ts +1 -1
- package/dist/adapter/deconz/driver/constants.d.ts.map +1 -1
- package/dist/adapter/ember/adapter/emberAdapter.d.ts +1 -1
- package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
- package/dist/adapter/ember/adapter/emberAdapter.js +19 -10
- package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -1
- package/dist/adapter/ember/adapter/oneWaitress.d.ts +2 -0
- package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -1
- package/dist/adapter/ember/adapter/oneWaitress.js +13 -5
- package/dist/adapter/ember/adapter/oneWaitress.js.map +1 -1
- package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +1 -3
- package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
- package/dist/adapter/ezsp/adapter/ezspAdapter.js +17 -30
- package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
- package/dist/adapter/ezsp/driver/index.d.ts +1 -1
- package/dist/adapter/ezsp/driver/index.d.ts.map +1 -1
- package/dist/adapter/ezsp/driver/index.js +1 -1
- package/dist/adapter/ezsp/driver/index.js.map +1 -1
- package/dist/adapter/ezsp/driver/types/index.d.ts +1 -1
- package/dist/adapter/ezsp/driver/types/index.d.ts.map +1 -1
- package/dist/adapter/ezsp/driver/types/index.js +3 -3
- package/dist/adapter/ezsp/driver/types/index.js.map +1 -1
- package/dist/adapter/serialPort.d.ts.map +1 -1
- package/dist/adapter/serialPort.js +7 -0
- package/dist/adapter/serialPort.js.map +1 -1
- package/dist/adapter/z-stack/adapter/adapter-backup.js +1 -1
- package/dist/adapter/z-stack/adapter/adapter-backup.js.map +1 -1
- package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +1 -1
- package/dist/adapter/z-stack/adapter/adapter-nv-memory.js.map +1 -1
- package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -1
- package/dist/adapter/z-stack/adapter/manager.js +12 -2
- package/dist/adapter/z-stack/adapter/manager.js.map +1 -1
- package/dist/adapter/z-stack/adapter/tstype.d.ts.map +1 -1
- package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +1 -3
- package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
- package/dist/adapter/z-stack/adapter/zStackAdapter.js +20 -34
- package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
- package/dist/adapter/z-stack/constants/index.d.ts +1 -1
- package/dist/adapter/z-stack/constants/index.d.ts.map +1 -1
- package/dist/adapter/z-stack/constants/index.js +1 -1
- package/dist/adapter/z-stack/constants/index.js.map +1 -1
- package/dist/adapter/z-stack/unpi/constants.d.ts +1 -1
- package/dist/adapter/z-stack/unpi/constants.d.ts.map +1 -1
- package/dist/adapter/z-stack/unpi/constants.js +1 -1
- package/dist/adapter/z-stack/unpi/constants.js.map +1 -1
- package/dist/adapter/zboss/adapter/zbossAdapter.d.ts +7 -8
- package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -1
- package/dist/adapter/zboss/adapter/zbossAdapter.js +12 -30
- package/dist/adapter/zboss/adapter/zbossAdapter.js.map +1 -1
- package/dist/adapter/zboss/driver.d.ts.map +1 -1
- package/dist/adapter/zboss/driver.js +8 -1
- package/dist/adapter/zboss/driver.js.map +1 -1
- package/dist/adapter/zboss/uart.d.ts.map +1 -1
- package/dist/adapter/zboss/uart.js +14 -2
- package/dist/adapter/zboss/uart.js.map +1 -1
- package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +1 -3
- package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -1
- package/dist/adapter/zigate/adapter/zigateAdapter.js +8 -29
- package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
- package/dist/adapter/zoh/adapter/zohAdapter.d.ts +1 -3
- package/dist/adapter/zoh/adapter/zohAdapter.d.ts.map +1 -1
- package/dist/adapter/zoh/adapter/zohAdapter.js +18 -33
- package/dist/adapter/zoh/adapter/zohAdapter.js.map +1 -1
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/controller/controller.js +10 -2
- package/dist/controller/controller.js.map +1 -1
- package/dist/controller/greenPower.d.ts.map +1 -1
- package/dist/controller/greenPower.js +15 -9
- package/dist/controller/greenPower.js.map +1 -1
- package/dist/controller/helpers/ota.d.ts +4 -4
- package/dist/controller/helpers/ota.d.ts.map +1 -1
- package/dist/controller/helpers/ota.js +28 -9
- package/dist/controller/helpers/ota.js.map +1 -1
- package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
- package/dist/controller/helpers/zclFrameConverter.js +17 -16
- package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
- package/dist/controller/model/device.d.ts +14 -3
- package/dist/controller/model/device.d.ts.map +1 -1
- package/dist/controller/model/device.js +155 -68
- package/dist/controller/model/device.js.map +1 -1
- package/dist/controller/model/endpoint.d.ts +7 -3
- package/dist/controller/model/endpoint.d.ts.map +1 -1
- package/dist/controller/model/endpoint.js +34 -21
- package/dist/controller/model/endpoint.js.map +1 -1
- package/dist/controller/model/group.js +4 -4
- package/dist/controller/model/group.js.map +1 -1
- package/dist/controller/touchlink.js +3 -3
- package/dist/controller/touchlink.js.map +1 -1
- package/dist/utils/timeService.js +2 -2
- package/dist/utils/timeService.js.map +1 -1
- package/dist/zspec/zcl/buffaloZcl.d.ts +3 -3
- package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
- package/dist/zspec/zcl/buffaloZcl.js +198 -96
- package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
- package/dist/zspec/zcl/definition/cluster.d.ts +2 -2
- package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/cluster.js +2699 -2808
- package/dist/zspec/zcl/definition/cluster.js.map +1 -1
- package/dist/zspec/zcl/definition/clusters-types.d.ts +63 -1109
- package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/enums.d.ts +0 -1
- package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/enums.js +0 -1
- package/dist/zspec/zcl/definition/enums.js.map +1 -1
- package/dist/zspec/zcl/definition/foundation.d.ts +306 -7
- package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/foundation.js +552 -207
- package/dist/zspec/zcl/definition/foundation.js.map +1 -1
- package/dist/zspec/zcl/definition/status.d.ts +21 -10
- package/dist/zspec/zcl/definition/status.d.ts.map +1 -1
- package/dist/zspec/zcl/definition/status.js +11 -0
- package/dist/zspec/zcl/definition/status.js.map +1 -1
- package/dist/zspec/zcl/definition/tstype.d.ts +57 -48
- package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
- package/dist/zspec/zcl/utils.d.ts +7 -4
- package/dist/zspec/zcl/utils.d.ts.map +1 -1
- package/dist/zspec/zcl/utils.js +133 -240
- package/dist/zspec/zcl/utils.js.map +1 -1
- package/dist/zspec/zcl/zclFrame.d.ts +4 -4
- package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
- package/dist/zspec/zcl/zclFrame.js +19 -103
- package/dist/zspec/zcl/zclFrame.js.map +1 -1
- package/dist/zspec/zcl/zclStatusError.d.ts +1 -1
- package/dist/zspec/zcl/zclStatusError.d.ts.map +1 -1
- package/dist/zspec/zcl/zclStatusError.js +2 -2
- package/dist/zspec/zcl/zclStatusError.js.map +1 -1
- package/package.json +1 -1
- package/scripts/clusters-typegen.ts +44 -139
- package/src/adapter/adapter.ts +38 -3
- package/src/adapter/adapterDiscovery.ts +2 -1
- package/src/adapter/deconz/adapter/deconzAdapter.ts +24 -51
- package/src/adapter/deconz/driver/constants.ts +1 -1
- package/src/adapter/ember/adapter/emberAdapter.ts +23 -10
- package/src/adapter/ember/adapter/oneWaitress.ts +16 -6
- package/src/adapter/ezsp/adapter/ezspAdapter.ts +27 -48
- package/src/adapter/ezsp/driver/index.ts +1 -1
- package/src/adapter/ezsp/driver/types/index.ts +99 -99
- package/src/adapter/serialPort.ts +9 -0
- package/src/adapter/z-stack/adapter/adapter-backup.ts +1 -1
- package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +1 -1
- package/src/adapter/z-stack/adapter/manager.ts +16 -2
- package/src/adapter/z-stack/adapter/tstype.ts +1 -0
- package/src/adapter/z-stack/adapter/zStackAdapter.ts +34 -81
- package/src/adapter/z-stack/constants/index.ts +1 -1
- package/src/adapter/z-stack/unpi/constants.ts +1 -1
- package/src/adapter/zboss/adapter/zbossAdapter.ts +23 -54
- package/src/adapter/zboss/driver.ts +8 -1
- package/src/adapter/zboss/uart.ts +14 -1
- package/src/adapter/zigate/adapter/zigateAdapter.ts +17 -48
- package/src/adapter/zoh/adapter/zohAdapter.ts +27 -50
- package/src/controller/controller.ts +12 -2
- package/src/controller/greenPower.ts +16 -9
- package/src/controller/helpers/ota.ts +37 -11
- package/src/controller/helpers/zclFrameConverter.ts +20 -17
- package/src/controller/model/device.ts +192 -79
- package/src/controller/model/endpoint.ts +36 -24
- package/src/controller/model/group.ts +4 -4
- package/src/controller/touchlink.ts +3 -3
- package/src/utils/timeService.ts +2 -2
- package/src/zspec/zcl/buffaloZcl.ts +226 -100
- package/src/zspec/zcl/definition/cluster.ts +2713 -2822
- package/src/zspec/zcl/definition/clusters-types.ts +80 -1135
- package/src/zspec/zcl/definition/enums.ts +0 -1
- package/src/zspec/zcl/definition/foundation.ts +703 -216
- package/src/zspec/zcl/definition/status.ts +22 -11
- package/src/zspec/zcl/definition/tstype.ts +59 -58
- package/src/zspec/zcl/utils.ts +137 -264
- package/src/zspec/zcl/zclFrame.ts +25 -130
- package/src/zspec/zcl/zclStatusError.ts +2 -2
- package/test/adapter/ember/emberAdapter.test.ts +191 -4
- package/test/adapter/ezsp/uart.test.ts +10 -10
- package/test/adapter/z-stack/adapter.test.ts +88 -32
- package/test/adapter/zoh/zohAdapter.test.ts +4 -4
- package/test/controller.test.ts +822 -248
- package/test/device-ota.test.ts +141 -16
- package/test/device.test.ts +731 -0
- package/test/requests.bench.ts +2 -0
- package/test/zcl.test.ts +70 -95
- package/test/zspec/zcl/buffalo.test.ts +251 -11
- package/test/zspec/zcl/foundation.test.ts +990 -0
- package/test/zspec/zcl/frame.test.ts +84 -69
- package/test/zspec/zcl/utils.test.ts +105 -81
- package/tsconfig.json +0 -1
- package/scripts/check-clusters-changes.ts +0 -328
- package/scripts/clusters-changes.log +0 -584
- package/scripts/utils.ts +0 -88
- package/scripts/zap-update-clusters-report.json +0 -303
- package/scripts/zap-update-clusters.ts +0 -1520
- package/scripts/zap-update-types.ts +0 -707
- package/scripts/zap-xml-clusters-overrides-data.ts +0 -52
- package/scripts/zap-xml-clusters-overrides.ts +0 -400
- package/scripts/zap-xml-types.ts +0 -146
package/test/controller.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import Request from "../src/controller/helpers/request";
|
|
|
10
10
|
import zclTransactionSequenceNumber from "../src/controller/helpers/zclTransactionSequenceNumber";
|
|
11
11
|
import {Device, Endpoint, Group} from "../src/controller/model";
|
|
12
12
|
import {InterviewState} from "../src/controller/model/device";
|
|
13
|
+
import type {TCustomCluster} from "../src/controller/tstype";
|
|
13
14
|
import type * as Models from "../src/models";
|
|
14
15
|
import * as Utils from "../src/utils";
|
|
15
16
|
import {setLogger} from "../src/utils/logger";
|
|
@@ -207,9 +208,11 @@ const mockAdapterSendZdo = vi
|
|
|
207
208
|
|
|
208
209
|
let iasZoneReadState170Count = 0;
|
|
209
210
|
let enroll170 = true;
|
|
211
|
+
let enrollRspThrow = false;
|
|
210
212
|
let configureReportStatus = 0;
|
|
211
213
|
let configureReportDefaultRsp = false;
|
|
212
214
|
let lastSentZclFrameToEndpoint: Buffer | undefined;
|
|
215
|
+
let readManufacturerCode: number | undefined;
|
|
213
216
|
|
|
214
217
|
const restoreMocksendZclFrameToEndpoint = () => {
|
|
215
218
|
mocksendZclFrameToEndpoint.mockImplementation((_ieeeAddr, networkAddress, endpoint, frame: Zcl.Frame) => {
|
|
@@ -223,24 +226,31 @@ const restoreMocksendZclFrameToEndpoint = () => {
|
|
|
223
226
|
const payload: {[key: string]: unknown}[] = [];
|
|
224
227
|
const cluster = frame.cluster;
|
|
225
228
|
for (const item of frame.payload) {
|
|
226
|
-
if (item.attrId
|
|
227
|
-
|
|
229
|
+
if (item.attrId === 65314) {
|
|
230
|
+
payload.push({
|
|
231
|
+
attrId: item.attrId,
|
|
232
|
+
status: Zcl.Status.SUCCESS,
|
|
233
|
+
dataType: Zcl.DataType.UINT16,
|
|
234
|
+
attrData: 0x4312,
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
const attribute = Zcl.Utils.getClusterAttribute(cluster, item.attrId, readManufacturerCode);
|
|
228
238
|
|
|
229
239
|
if (attribute) {
|
|
230
240
|
if (frame.isCluster("ssIasZone") && item.attrId === 0) {
|
|
231
241
|
iasZoneReadState170Count++;
|
|
232
242
|
payload.push({
|
|
233
243
|
attrId: item.attrId,
|
|
244
|
+
status: Zcl.Status.SUCCESS,
|
|
234
245
|
dataType: attribute.type,
|
|
235
246
|
attrData: iasZoneReadState170Count === 2 && enroll170 ? 1 : 0,
|
|
236
|
-
status: 0,
|
|
237
247
|
});
|
|
238
248
|
} else {
|
|
239
249
|
payload.push({
|
|
240
250
|
attrId: item.attrId,
|
|
251
|
+
status: Zcl.Status.SUCCESS,
|
|
241
252
|
dataType: attribute.type,
|
|
242
253
|
attrData: MOCK_DEVICES[networkAddress]!.attributes![endpoint][attribute.name],
|
|
243
|
-
status: 0,
|
|
244
254
|
});
|
|
245
255
|
}
|
|
246
256
|
}
|
|
@@ -260,12 +270,16 @@ const restoreMocksendZclFrameToEndpoint = () => {
|
|
|
260
270
|
10,
|
|
261
271
|
`${frame.command.name}Rsp`,
|
|
262
272
|
frame.cluster.ID,
|
|
263
|
-
{status:
|
|
273
|
+
{status: Zcl.Status.SUCCESS, groupid: 1},
|
|
264
274
|
{},
|
|
265
275
|
);
|
|
266
276
|
return {clusterID: frame.cluster.ID, header: responseFrame.header, data: responseFrame.toBuffer()};
|
|
267
277
|
}
|
|
268
278
|
|
|
279
|
+
if (frame.header.isSpecific && frame.command.name === "enrollRsp" && frame.cluster.name === "ssIasZone" && enrollRspThrow) {
|
|
280
|
+
throw new Error("ias-zone-failure");
|
|
281
|
+
}
|
|
282
|
+
|
|
269
283
|
if (
|
|
270
284
|
networkAddress === 170 &&
|
|
271
285
|
frame.header.isGlobal &&
|
|
@@ -301,7 +315,7 @@ const restoreMocksendZclFrameToEndpoint = () => {
|
|
|
301
315
|
if (frame.header.isGlobal && frame.isCommand("write")) {
|
|
302
316
|
const payload: {[key: string]: unknown}[] = [];
|
|
303
317
|
for (const item of frame.payload) {
|
|
304
|
-
payload.push({attrId: item.attrId, status:
|
|
318
|
+
payload.push({attrId: item.attrId, status: Zcl.Status.SUCCESS});
|
|
305
319
|
}
|
|
306
320
|
|
|
307
321
|
const responseFrame = Zcl.Frame.create(0, 1, true, undefined, 10, "writeRsp", 0, payload, {});
|
|
@@ -460,6 +474,78 @@ const options = {
|
|
|
460
474
|
|
|
461
475
|
const databaseContents = () => fs.readFileSync(options.databasePath).toString();
|
|
462
476
|
|
|
477
|
+
const CUSTOM_CLUSTERS = {
|
|
478
|
+
hvacThermostat: {
|
|
479
|
+
name: "hvacThermostat",
|
|
480
|
+
ID: 0x0201,
|
|
481
|
+
attributes: {
|
|
482
|
+
viessmannWindowOpenInternal: {
|
|
483
|
+
name: "viessmannWindowOpenInternal",
|
|
484
|
+
ID: 0x4000,
|
|
485
|
+
type: Zcl.DataType.ENUM8,
|
|
486
|
+
manufacturerCode: Zcl.ManufacturerCode.VIESSMANN_ELEKTRONIK_GMBH,
|
|
487
|
+
write: true,
|
|
488
|
+
max: 0xff,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
commands: {},
|
|
492
|
+
commandsResponse: {},
|
|
493
|
+
},
|
|
494
|
+
closuresWindowCovering: {
|
|
495
|
+
name: "closuresWindowCovering",
|
|
496
|
+
ID: Zcl.Clusters.closuresWindowCovering.ID,
|
|
497
|
+
attributes: {
|
|
498
|
+
calibrationMode: {
|
|
499
|
+
name: "calibrationMode",
|
|
500
|
+
ID: 0xf002,
|
|
501
|
+
type: Zcl.DataType.ENUM8,
|
|
502
|
+
manufacturerCode: Zcl.ManufacturerCode.LEGRAND_GROUP,
|
|
503
|
+
write: true,
|
|
504
|
+
max: 0xff,
|
|
505
|
+
},
|
|
506
|
+
tuyaMotorReversal: {name: "tuyaMotorReversal", ID: 0xf002, type: Zcl.DataType.ENUM8, write: true, max: 0xff},
|
|
507
|
+
},
|
|
508
|
+
commands: {},
|
|
509
|
+
commandsResponse: {},
|
|
510
|
+
},
|
|
511
|
+
lightingColorCtrl: {
|
|
512
|
+
name: "lightingColorCtrl",
|
|
513
|
+
ID: Zcl.Clusters.lightingColorCtrl.ID,
|
|
514
|
+
attributes: {},
|
|
515
|
+
commands: {
|
|
516
|
+
tuyaMoveToHueAndSaturationBrightness: {
|
|
517
|
+
name: "tuyaMoveToHueAndSaturationBrightness",
|
|
518
|
+
ID: 0x06,
|
|
519
|
+
parameters: [
|
|
520
|
+
{name: "hue", type: Zcl.DataType.UINT8, max: 0xff},
|
|
521
|
+
{name: "saturation", type: Zcl.DataType.UINT8, max: 0xff},
|
|
522
|
+
{name: "transtime", type: Zcl.DataType.UINT16, max: 0xffff},
|
|
523
|
+
{name: "brightness", type: Zcl.DataType.UINT8, max: 0xff},
|
|
524
|
+
],
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
commandsResponse: {},
|
|
528
|
+
},
|
|
529
|
+
} satisfies CustomClusters;
|
|
530
|
+
|
|
531
|
+
interface CustomClustersTypes extends Record<string, TCustomCluster> {
|
|
532
|
+
hvacThermostat: {
|
|
533
|
+
attributes: {
|
|
534
|
+
viessmannWindowOpenInternal: number;
|
|
535
|
+
};
|
|
536
|
+
commands: never;
|
|
537
|
+
commandResponses: never;
|
|
538
|
+
};
|
|
539
|
+
closuresWindowCovering: {
|
|
540
|
+
attributes: {
|
|
541
|
+
calibrationMode: number;
|
|
542
|
+
tuyaMotorReversal: number;
|
|
543
|
+
};
|
|
544
|
+
commands: never;
|
|
545
|
+
commandResponses: never;
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
463
549
|
describe("Controller", () => {
|
|
464
550
|
let controller: Controller;
|
|
465
551
|
let mockedDate: Date;
|
|
@@ -480,6 +566,7 @@ describe("Controller", () => {
|
|
|
480
566
|
|
|
481
567
|
beforeEach(() => {
|
|
482
568
|
vi.setSystemTime(mockedDate);
|
|
569
|
+
readManufacturerCode = undefined;
|
|
483
570
|
sendZdoResponseStatus = Zdo.Status.SUCCESS;
|
|
484
571
|
for (const m of mocksRestore) m.mockRestore();
|
|
485
572
|
for (const m of mocksClear) m.mockClear();
|
|
@@ -490,6 +577,7 @@ describe("Controller", () => {
|
|
|
490
577
|
configureReportStatus = 0;
|
|
491
578
|
configureReportDefaultRsp = false;
|
|
492
579
|
enroll170 = true;
|
|
580
|
+
enrollRspThrow = false;
|
|
493
581
|
options.network.channelList = [15];
|
|
494
582
|
|
|
495
583
|
for (const event in events) {
|
|
@@ -2297,8 +2385,7 @@ describe("Controller", () => {
|
|
|
2297
2385
|
|
|
2298
2386
|
it("Device joins and interview iAs enrollment succeeds", async () => {
|
|
2299
2387
|
await controller.start();
|
|
2300
|
-
|
|
2301
|
-
await event;
|
|
2388
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 170, ieeeAddr: "0x170"});
|
|
2302
2389
|
expect(events.deviceInterview.length).toBe(2);
|
|
2303
2390
|
expect(events.deviceInterview[0].status).toBe("started");
|
|
2304
2391
|
// @ts-expect-error private but deep cloned
|
|
@@ -2325,7 +2412,6 @@ describe("Controller", () => {
|
|
|
2325
2412
|
command: {
|
|
2326
2413
|
ID: 2,
|
|
2327
2414
|
name: "write",
|
|
2328
|
-
parameters: expect.any(Array),
|
|
2329
2415
|
response: 4,
|
|
2330
2416
|
},
|
|
2331
2417
|
});
|
|
@@ -2337,7 +2423,7 @@ describe("Controller", () => {
|
|
|
2337
2423
|
expect(deepClone(enrollRsp[3])).toStrictEqual({
|
|
2338
2424
|
header: {
|
|
2339
2425
|
frameControl: {reservedBits: 0, frameType: 1, direction: 0, disableDefaultResponse: true, manufacturerSpecific: false},
|
|
2340
|
-
transactionSequenceNumber:
|
|
2426
|
+
transactionSequenceNumber: 1,
|
|
2341
2427
|
commandIdentifier: 0,
|
|
2342
2428
|
},
|
|
2343
2429
|
payload: {enrollrspcode: 0, zoneid: 23},
|
|
@@ -2368,6 +2454,20 @@ describe("Controller", () => {
|
|
|
2368
2454
|
expect(events.deviceInterview[1].device._ieeeAddr).toBe("0x170");
|
|
2369
2455
|
});
|
|
2370
2456
|
|
|
2457
|
+
it("Device joins and interview iAs enrollment throws", async () => {
|
|
2458
|
+
await controller.start();
|
|
2459
|
+
enrollRspThrow = true;
|
|
2460
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 170, ieeeAddr: "0x170"});
|
|
2461
|
+
|
|
2462
|
+
expect(events.deviceInterview.length).toBe(2);
|
|
2463
|
+
expect(events.deviceInterview[0].status).toBe("started");
|
|
2464
|
+
// @ts-expect-error private but deep cloned
|
|
2465
|
+
expect(events.deviceInterview[0].device._ieeeAddr).toBe("0x170");
|
|
2466
|
+
expect(events.deviceInterview[1].status).toBe("failed");
|
|
2467
|
+
// @ts-expect-error private but deep cloned
|
|
2468
|
+
expect(events.deviceInterview[1].device._ieeeAddr).toBe("0x170");
|
|
2469
|
+
});
|
|
2470
|
+
|
|
2371
2471
|
it("Device joins, shouldnt enroll when already enrolled", async () => {
|
|
2372
2472
|
await controller.start();
|
|
2373
2473
|
iasZoneReadState170Count = 1;
|
|
@@ -2532,9 +2632,9 @@ describe("Controller", () => {
|
|
|
2532
2632
|
const endpointReadSpy = vi.spyOn(endpoint, "read");
|
|
2533
2633
|
const endpointDefaultResponseSpy = vi.spyOn(endpoint, "defaultResponse");
|
|
2534
2634
|
|
|
2535
|
-
deviceOnZclDataSpy.mockImplementationOnce(async (a, b, c) => {
|
|
2635
|
+
deviceOnZclDataSpy.mockImplementationOnce(async (a, b, c, d) => {
|
|
2536
2636
|
await mockAdapterEvents.deviceLeave({networkAddress: 129, ieeeAddr: undefined});
|
|
2537
|
-
await device.onZclData(a, b, c);
|
|
2637
|
+
await device.onZclData(a, b, c, d);
|
|
2538
2638
|
});
|
|
2539
2639
|
|
|
2540
2640
|
await mockAdapterEvents.zclPayload({
|
|
@@ -2600,8 +2700,17 @@ describe("Controller", () => {
|
|
|
2600
2700
|
});
|
|
2601
2701
|
|
|
2602
2702
|
it("Receive cluster command", async () => {
|
|
2603
|
-
const
|
|
2604
|
-
|
|
2703
|
+
const frame = Zcl.Frame.create(
|
|
2704
|
+
Zcl.FrameType.SPECIFIC,
|
|
2705
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
2706
|
+
false,
|
|
2707
|
+
undefined,
|
|
2708
|
+
29,
|
|
2709
|
+
"addRsp",
|
|
2710
|
+
"genScenes",
|
|
2711
|
+
{status: 0, groupId: 10, sceneId: 2},
|
|
2712
|
+
{},
|
|
2713
|
+
);
|
|
2605
2714
|
await controller.start();
|
|
2606
2715
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
2607
2716
|
await mockAdapterEvents.zclPayload({
|
|
@@ -2618,24 +2727,25 @@ describe("Controller", () => {
|
|
|
2618
2727
|
expect(events.message.length).toBe(1);
|
|
2619
2728
|
const expected = {
|
|
2620
2729
|
cluster: "genScenes",
|
|
2621
|
-
type: "
|
|
2730
|
+
type: "commandAddRsp",
|
|
2622
2731
|
device: expect.any(Device),
|
|
2623
2732
|
endpoint: expect.any(Endpoint),
|
|
2624
2733
|
data: {
|
|
2625
|
-
|
|
2626
|
-
|
|
2734
|
+
groupId: 10,
|
|
2735
|
+
sceneId: 2,
|
|
2736
|
+
status: 0,
|
|
2627
2737
|
},
|
|
2628
2738
|
linkquality: 19,
|
|
2629
2739
|
groupID: 10,
|
|
2630
2740
|
meta: {
|
|
2631
2741
|
zclTransactionSequenceNumber: 29,
|
|
2632
|
-
manufacturerCode:
|
|
2742
|
+
manufacturerCode: undefined,
|
|
2633
2743
|
frameControl: {
|
|
2634
2744
|
reservedBits: 0,
|
|
2635
|
-
direction:
|
|
2745
|
+
direction: 1,
|
|
2636
2746
|
disableDefaultResponse: false,
|
|
2637
2747
|
frameType: 1,
|
|
2638
|
-
manufacturerSpecific:
|
|
2748
|
+
manufacturerSpecific: false,
|
|
2639
2749
|
},
|
|
2640
2750
|
rawData: expect.any(Buffer),
|
|
2641
2751
|
},
|
|
@@ -2680,21 +2790,14 @@ describe("Controller", () => {
|
|
|
2680
2790
|
|
|
2681
2791
|
it("Receive zclData send default response", async () => {
|
|
2682
2792
|
const frame = Zcl.Frame.create(
|
|
2683
|
-
|
|
2684
|
-
|
|
2793
|
+
Zcl.FrameType.SPECIFIC,
|
|
2794
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
2685
2795
|
false,
|
|
2686
|
-
|
|
2796
|
+
Zcl.ManufacturerCode.IKEA_OF_SWEDEN,
|
|
2687
2797
|
29,
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
{
|
|
2691
|
-
groupid: 1,
|
|
2692
|
-
sceneid: 1,
|
|
2693
|
-
status: 0,
|
|
2694
|
-
transtime: 0,
|
|
2695
|
-
scenename: "",
|
|
2696
|
-
extensionfieldsets: [],
|
|
2697
|
-
},
|
|
2798
|
+
"viewRsp",
|
|
2799
|
+
"genScenes",
|
|
2800
|
+
{groupid: 1, sceneid: 1, status: 0, transtime: 0, scenename: "", extensionfieldsets: []},
|
|
2698
2801
|
{},
|
|
2699
2802
|
);
|
|
2700
2803
|
await controller.start();
|
|
@@ -2986,11 +3089,8 @@ describe("Controller", () => {
|
|
|
2986
3089
|
it("Respond to read of attribute", async () => {
|
|
2987
3090
|
await controller.start();
|
|
2988
3091
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
2989
|
-
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
2990
|
-
const endpoint = device.getEndpoint(1)!;
|
|
2991
|
-
endpoint.saveClusterAttributeKeyValue("hvacThermostat", {systemMode: 3});
|
|
2992
3092
|
mocksendZclFrameToEndpoint.mockClear();
|
|
2993
|
-
const frame = Zcl.Frame.create(0, 0, true, undefined, 40,
|
|
3093
|
+
const frame = Zcl.Frame.create(0, 0, true, undefined, 40, "read", "genTime", [{attrId: 0x0000}, {attrId: 0xfffa}], {});
|
|
2994
3094
|
await mockAdapterEvents.zclPayload({
|
|
2995
3095
|
wasBroadcast: false,
|
|
2996
3096
|
address: 129,
|
|
@@ -3013,12 +3113,38 @@ describe("Controller", () => {
|
|
|
3013
3113
|
transactionSequenceNumber: 40,
|
|
3014
3114
|
commandIdentifier: 1,
|
|
3015
3115
|
},
|
|
3016
|
-
payload: [
|
|
3116
|
+
payload: [
|
|
3117
|
+
{attrId: 0xfffa, status: Zcl.Status.UNSUPPORTED_ATTRIBUTE},
|
|
3118
|
+
{attrId: 0x0000, attrData: expect.any(Number), dataType: Zcl.DataType.UTC, status: Zcl.Status.SUCCESS},
|
|
3119
|
+
],
|
|
3017
3120
|
cluster: null,
|
|
3018
|
-
command: expect.objectContaining({
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3121
|
+
command: expect.objectContaining({ID: 1, name: "readRsp"}),
|
|
3122
|
+
});
|
|
3123
|
+
});
|
|
3124
|
+
|
|
3125
|
+
it("Sends read response for unsupported attribute", async () => {
|
|
3126
|
+
await controller.start();
|
|
3127
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
3128
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
3129
|
+
const endpoint = device.getEndpoint(1)!;
|
|
3130
|
+
mocksendZclFrameToEndpoint.mockClear();
|
|
3131
|
+
|
|
3132
|
+
endpoint.readResponse("genGroups", 1, {nameSupport: undefined}, {srcEndpoint: 1});
|
|
3133
|
+
|
|
3134
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
3135
|
+
const call = mocksendZclFrameToEndpoint.mock.calls[0];
|
|
3136
|
+
expect(call[0]).toBe("0x129");
|
|
3137
|
+
expect(call[1]).toBe(129);
|
|
3138
|
+
expect(call[2]).toBe(1);
|
|
3139
|
+
expect(deepClone({...call[3], cluster: null})).toStrictEqual({
|
|
3140
|
+
header: {
|
|
3141
|
+
frameControl: {reservedBits: 0, frameType: 0, direction: 1, disableDefaultResponse: true, manufacturerSpecific: false},
|
|
3142
|
+
transactionSequenceNumber: 1,
|
|
3143
|
+
commandIdentifier: 1,
|
|
3144
|
+
},
|
|
3145
|
+
payload: [{attrId: 0x0000, status: Zcl.Status.UNSUPPORTED_ATTRIBUTE}],
|
|
3146
|
+
cluster: null,
|
|
3147
|
+
command: expect.objectContaining({ID: 1, name: "readRsp"}),
|
|
3022
3148
|
});
|
|
3023
3149
|
});
|
|
3024
3150
|
|
|
@@ -3330,12 +3456,13 @@ describe("Controller", () => {
|
|
|
3330
3456
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
3331
3457
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
3332
3458
|
device.addCustomCluster("genBasic", {
|
|
3459
|
+
name: "genBasic",
|
|
3333
3460
|
ID: 0,
|
|
3334
3461
|
commands: {},
|
|
3335
3462
|
commandsResponse: {},
|
|
3336
3463
|
attributes: {
|
|
3337
|
-
customAttr: {ID: 256, type: Zcl.DataType.UINT8},
|
|
3338
|
-
aDifferentZclVersion: {ID: 0, type: Zcl.DataType.UINT8},
|
|
3464
|
+
customAttr: {name: "customAttr", ID: 256, type: Zcl.DataType.UINT8},
|
|
3465
|
+
aDifferentZclVersion: {name: "aDifferentZclVersion", ID: 0, type: Zcl.DataType.UINT8},
|
|
3339
3466
|
},
|
|
3340
3467
|
});
|
|
3341
3468
|
const buffer = Buffer.from([24, 169, 10, 0, 1, 24, 3, 0, 0, 24, 1, 2, 0, 24, 1]);
|
|
@@ -3348,11 +3475,12 @@ describe("Controller", () => {
|
|
|
3348
3475
|
|
|
3349
3476
|
// Should allow to extend an already extended cluster again.
|
|
3350
3477
|
device.addCustomCluster("genBasic", {
|
|
3478
|
+
name: "genBasic",
|
|
3351
3479
|
ID: 0,
|
|
3352
3480
|
commands: {},
|
|
3353
3481
|
commandsResponse: {},
|
|
3354
3482
|
attributes: {
|
|
3355
|
-
customAttrSecondOverride: {ID: 256, type: Zcl.DataType.UINT8},
|
|
3483
|
+
customAttrSecondOverride: {name: "customAttrSecondOverride", ID: 256, type: Zcl.DataType.UINT8},
|
|
3356
3484
|
},
|
|
3357
3485
|
});
|
|
3358
3486
|
await mockAdapterEvents.zclPayload(payload);
|
|
@@ -3366,10 +3494,11 @@ describe("Controller", () => {
|
|
|
3366
3494
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
3367
3495
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
3368
3496
|
device.addCustomCluster("myCustomCluster", {
|
|
3497
|
+
name: "myCustomCluster",
|
|
3369
3498
|
ID: 9123,
|
|
3370
3499
|
commands: {},
|
|
3371
3500
|
commandsResponse: {},
|
|
3372
|
-
attributes: {superAttribute: {ID: 0, type: Zcl.DataType.UINT8}},
|
|
3501
|
+
attributes: {superAttribute: {name: "superAttribute", ID: 0, type: Zcl.DataType.UINT8}},
|
|
3373
3502
|
});
|
|
3374
3503
|
const buffer = Buffer.from([24, 169, 10, 0, 1, 24, 3, 0, 0, 24, 1]);
|
|
3375
3504
|
const header = Zcl.Header.fromBuffer(buffer);
|
|
@@ -3393,10 +3522,11 @@ describe("Controller", () => {
|
|
|
3393
3522
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
3394
3523
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
3395
3524
|
device.addCustomCluster("myCustomCluster", {
|
|
3525
|
+
name: "myCustomCluster",
|
|
3396
3526
|
ID: Zcl.Clusters.genBasic.ID,
|
|
3397
3527
|
commands: {},
|
|
3398
3528
|
commandsResponse: {},
|
|
3399
|
-
attributes: {customAttr: {ID: 256, type: Zcl.DataType.UINT8}},
|
|
3529
|
+
attributes: {customAttr: {name: "customAttr", ID: 256, type: Zcl.DataType.UINT8}},
|
|
3400
3530
|
});
|
|
3401
3531
|
const buffer = Buffer.from([24, 169, 10, 0, 1, 24, 3, 0, 0, 24, 1]);
|
|
3402
3532
|
const header = Zcl.Header.fromBuffer(buffer);
|
|
@@ -3462,17 +3592,28 @@ describe("Controller", () => {
|
|
|
3462
3592
|
}
|
|
3463
3593
|
|
|
3464
3594
|
device.addCustomCluster("hvacThermostat", {
|
|
3595
|
+
name: "hvacThermostat",
|
|
3465
3596
|
ID: 0x0201,
|
|
3466
3597
|
attributes: {
|
|
3467
|
-
localTemperatureCalibration: {
|
|
3598
|
+
localTemperatureCalibration: {
|
|
3599
|
+
name: "localTemperatureCalibration",
|
|
3600
|
+
ID: 0x0010,
|
|
3601
|
+
type: Zcl.DataType.INT8,
|
|
3602
|
+
write: true,
|
|
3603
|
+
min: -50,
|
|
3604
|
+
max: 50,
|
|
3605
|
+
default: 0,
|
|
3606
|
+
},
|
|
3468
3607
|
},
|
|
3469
3608
|
commands: {},
|
|
3470
3609
|
commandsResponse: {},
|
|
3471
3610
|
});
|
|
3472
3611
|
device.addCustomCluster("hvacThermostat", {
|
|
3612
|
+
name: "hvacThermostat",
|
|
3473
3613
|
ID: Zcl.Clusters.hvacThermostat.ID,
|
|
3474
3614
|
attributes: {
|
|
3475
3615
|
operatingMode: {
|
|
3616
|
+
name: "operatingMode",
|
|
3476
3617
|
ID: 0x4007,
|
|
3477
3618
|
type: Zcl.DataType.ENUM8,
|
|
3478
3619
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3480,6 +3621,7 @@ describe("Controller", () => {
|
|
|
3480
3621
|
max: 0xff,
|
|
3481
3622
|
},
|
|
3482
3623
|
heatingDemand: {
|
|
3624
|
+
name: "heatingDemand",
|
|
3483
3625
|
ID: 0x4020,
|
|
3484
3626
|
type: Zcl.DataType.ENUM8,
|
|
3485
3627
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3487,6 +3629,7 @@ describe("Controller", () => {
|
|
|
3487
3629
|
max: 0xff,
|
|
3488
3630
|
},
|
|
3489
3631
|
valveAdaptStatus: {
|
|
3632
|
+
name: "valveAdaptStatus",
|
|
3490
3633
|
ID: 0x4022,
|
|
3491
3634
|
type: Zcl.DataType.ENUM8,
|
|
3492
3635
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3494,6 +3637,7 @@ describe("Controller", () => {
|
|
|
3494
3637
|
max: 0xff,
|
|
3495
3638
|
},
|
|
3496
3639
|
unknownAttribute0: {
|
|
3640
|
+
name: "unknownAttribute0",
|
|
3497
3641
|
ID: 0x4025,
|
|
3498
3642
|
type: Zcl.DataType.ENUM8,
|
|
3499
3643
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3501,6 +3645,7 @@ describe("Controller", () => {
|
|
|
3501
3645
|
max: 0xff,
|
|
3502
3646
|
},
|
|
3503
3647
|
remoteTemperature: {
|
|
3648
|
+
name: "remoteTemperature",
|
|
3504
3649
|
ID: 0x4040,
|
|
3505
3650
|
type: Zcl.DataType.INT16,
|
|
3506
3651
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3508,6 +3653,7 @@ describe("Controller", () => {
|
|
|
3508
3653
|
min: -32768,
|
|
3509
3654
|
},
|
|
3510
3655
|
unknownAttribute1: {
|
|
3656
|
+
name: "unknownAttribute1",
|
|
3511
3657
|
ID: 0x4041,
|
|
3512
3658
|
type: Zcl.DataType.ENUM8,
|
|
3513
3659
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3515,6 +3661,7 @@ describe("Controller", () => {
|
|
|
3515
3661
|
max: 0xff,
|
|
3516
3662
|
},
|
|
3517
3663
|
windowOpenMode: {
|
|
3664
|
+
name: "windowOpenMode",
|
|
3518
3665
|
ID: 0x4042,
|
|
3519
3666
|
type: Zcl.DataType.ENUM8,
|
|
3520
3667
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3522,6 +3669,7 @@ describe("Controller", () => {
|
|
|
3522
3669
|
max: 0xff,
|
|
3523
3670
|
},
|
|
3524
3671
|
boostHeating: {
|
|
3672
|
+
name: "boostHeating",
|
|
3525
3673
|
ID: 0x4043,
|
|
3526
3674
|
type: Zcl.DataType.ENUM8,
|
|
3527
3675
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3529,14 +3677,23 @@ describe("Controller", () => {
|
|
|
3529
3677
|
max: 0xff,
|
|
3530
3678
|
},
|
|
3531
3679
|
cableSensorTemperature: {
|
|
3680
|
+
name: "cableSensorTemperature",
|
|
3532
3681
|
ID: 0x4052,
|
|
3533
3682
|
type: Zcl.DataType.INT16,
|
|
3534
3683
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3535
3684
|
write: true,
|
|
3536
3685
|
min: -32768,
|
|
3537
3686
|
},
|
|
3538
|
-
valveType: {
|
|
3687
|
+
valveType: {
|
|
3688
|
+
name: "valveType",
|
|
3689
|
+
ID: 0x4060,
|
|
3690
|
+
type: Zcl.DataType.ENUM8,
|
|
3691
|
+
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3692
|
+
write: true,
|
|
3693
|
+
max: 0xff,
|
|
3694
|
+
},
|
|
3539
3695
|
unknownAttribute2: {
|
|
3696
|
+
name: "unknownAttribute2",
|
|
3540
3697
|
ID: 0x4061,
|
|
3541
3698
|
type: Zcl.DataType.ENUM8,
|
|
3542
3699
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3544,15 +3701,30 @@ describe("Controller", () => {
|
|
|
3544
3701
|
max: 0xff,
|
|
3545
3702
|
},
|
|
3546
3703
|
cableSensorMode: {
|
|
3704
|
+
name: "cableSensorMode",
|
|
3547
3705
|
ID: 0x4062,
|
|
3548
3706
|
type: Zcl.DataType.ENUM8,
|
|
3549
3707
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3550
3708
|
write: true,
|
|
3551
3709
|
max: 0xff,
|
|
3552
3710
|
},
|
|
3553
|
-
heaterType: {
|
|
3554
|
-
|
|
3711
|
+
heaterType: {
|
|
3712
|
+
name: "heaterType",
|
|
3713
|
+
ID: 0x4063,
|
|
3714
|
+
type: Zcl.DataType.ENUM8,
|
|
3715
|
+
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3716
|
+
write: true,
|
|
3717
|
+
max: 0xff,
|
|
3718
|
+
},
|
|
3719
|
+
errorState: {
|
|
3720
|
+
name: "errorState",
|
|
3721
|
+
ID: 0x5000,
|
|
3722
|
+
type: Zcl.DataType.BITMAP8,
|
|
3723
|
+
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3724
|
+
write: true,
|
|
3725
|
+
},
|
|
3555
3726
|
automaticValveAdapt: {
|
|
3727
|
+
name: "automaticValveAdapt",
|
|
3556
3728
|
ID: 0x5010,
|
|
3557
3729
|
type: Zcl.DataType.ENUM8,
|
|
3558
3730
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
@@ -3561,7 +3733,7 @@ describe("Controller", () => {
|
|
|
3561
3733
|
},
|
|
3562
3734
|
},
|
|
3563
3735
|
commands: {
|
|
3564
|
-
calibrateValve: {ID: 0x41, parameters: []},
|
|
3736
|
+
calibrateValve: {name: "calibrateValve", ID: 0x41, parameters: []},
|
|
3565
3737
|
},
|
|
3566
3738
|
commandsResponse: {},
|
|
3567
3739
|
});
|
|
@@ -3590,11 +3762,13 @@ describe("Controller", () => {
|
|
|
3590
3762
|
),
|
|
3591
3763
|
).rejects.toThrow("localTemperatureCalibration requires max of 50");
|
|
3592
3764
|
|
|
3765
|
+
readManufacturerCode = Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH;
|
|
3593
3766
|
await expect(
|
|
3594
3767
|
endpoint.read<"hvacThermostat", BoschThermostatCluster>("hvacThermostat", ["boostHeating", "operatingMode"], {
|
|
3595
3768
|
manufacturerCode: Zcl.ManufacturerCode.ROBERT_BOSCH_GMBH,
|
|
3596
3769
|
}),
|
|
3597
3770
|
).resolves.toStrictEqual({16391: 0, 16451: 0});
|
|
3771
|
+
readManufacturerCode = undefined;
|
|
3598
3772
|
});
|
|
3599
3773
|
|
|
3600
3774
|
it("Send zcl command to all no options", async () => {
|
|
@@ -3625,8 +3799,11 @@ describe("Controller", () => {
|
|
|
3625
3799
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
3626
3800
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
3627
3801
|
device.addCustomCluster("ssIasZone", {
|
|
3802
|
+
name: "ssIasZone",
|
|
3628
3803
|
ID: Zcl.Clusters.ssIasZone.ID,
|
|
3629
|
-
commands: {
|
|
3804
|
+
commands: {
|
|
3805
|
+
boschSmokeAlarmSiren: {name: "boschSmokeAlarmSiren", ID: 0x80, parameters: [{name: "data", type: Zcl.DataType.UINT16, max: 0xffff}]},
|
|
3806
|
+
},
|
|
3630
3807
|
commandsResponse: {},
|
|
3631
3808
|
attributes: {},
|
|
3632
3809
|
});
|
|
@@ -4032,13 +4209,24 @@ describe("Controller", () => {
|
|
|
4032
4209
|
expect(device.checkinInterval).toBeUndefined();
|
|
4033
4210
|
expect(device.pendingRequestTimeout).toStrictEqual(0);
|
|
4034
4211
|
mocksendZclFrameToEndpoint.mockClear();
|
|
4035
|
-
mocksendZclFrameToEndpoint.mockReturnValueOnce(
|
|
4212
|
+
mocksendZclFrameToEndpoint.mockReturnValueOnce(undefined);
|
|
4213
|
+
const newCheckinInterval = 204;
|
|
4036
4214
|
mocksendZclFrameToEndpoint.mockImplementationOnce((_ieeeAddr, _networkAddress, _endpoint, frame: Zcl.Frame) => {
|
|
4037
|
-
const payload = [
|
|
4215
|
+
const payload = [
|
|
4216
|
+
{
|
|
4217
|
+
attrId: Zcl.Clusters.genPollCtrl.attributes.checkinInterval.ID,
|
|
4218
|
+
status: Zcl.Status.SUCCESS,
|
|
4219
|
+
dataType: Zcl.DataType.UINT32,
|
|
4220
|
+
attrData: newCheckinInterval,
|
|
4221
|
+
},
|
|
4222
|
+
];
|
|
4038
4223
|
const responseFrame = Zcl.Frame.create(0, 1, true, undefined, 10, "readRsp", frame.cluster.ID, payload, {});
|
|
4039
4224
|
return {header: responseFrame.header, data: responseFrame.toBuffer(), clusterID: frame.cluster.ID};
|
|
4040
4225
|
});
|
|
4041
|
-
mocksendZclFrameToEndpoint.mockImplementationOnce(() =>
|
|
4226
|
+
mocksendZclFrameToEndpoint.mockImplementationOnce(() => {
|
|
4227
|
+
vi.advanceTimersByTime(10);
|
|
4228
|
+
return undefined;
|
|
4229
|
+
});
|
|
4042
4230
|
let frame = Zcl.Frame.create(
|
|
4043
4231
|
Zcl.FrameType.SPECIFIC,
|
|
4044
4232
|
Zcl.Direction.SERVER_TO_CLIENT,
|
|
@@ -4061,10 +4249,9 @@ describe("Controller", () => {
|
|
|
4061
4249
|
groupID: undefined,
|
|
4062
4250
|
});
|
|
4063
4251
|
await flushPromises();
|
|
4064
|
-
expect(device.checkinInterval).toStrictEqual(
|
|
4065
|
-
expect(device.pendingRequestTimeout).toStrictEqual(
|
|
4252
|
+
expect(device.checkinInterval).toStrictEqual(newCheckinInterval / 4);
|
|
4253
|
+
expect(device.pendingRequestTimeout).toStrictEqual((newCheckinInterval / 4) * 1000);
|
|
4066
4254
|
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(3);
|
|
4067
|
-
device.checkinInterval = 50;
|
|
4068
4255
|
|
|
4069
4256
|
mocksendZclFrameToEndpoint.mockClear();
|
|
4070
4257
|
frame = Zcl.Frame.create(
|
|
@@ -4095,6 +4282,36 @@ describe("Controller", () => {
|
|
|
4095
4282
|
expect(call[3].cluster.name).toBe("genPollCtrl");
|
|
4096
4283
|
expect(call[3].command.name).toBe("checkinRsp");
|
|
4097
4284
|
expect(call[3].payload).toStrictEqual({startFastPolling: 0, fastPollTimeout: 0});
|
|
4285
|
+
|
|
4286
|
+
await mockAdapterEvents.zdoResponse(Zdo.ClusterId.END_DEVICE_ANNOUNCE, [
|
|
4287
|
+
Zdo.Status.SUCCESS,
|
|
4288
|
+
{
|
|
4289
|
+
nwkAddress: 174,
|
|
4290
|
+
eui64: "0x174",
|
|
4291
|
+
capabilities: {
|
|
4292
|
+
allocateAddress: 0,
|
|
4293
|
+
alternatePANCoordinator: 0,
|
|
4294
|
+
deviceType: 0,
|
|
4295
|
+
powerSource: 0,
|
|
4296
|
+
reserved1: 0,
|
|
4297
|
+
reserved2: 0,
|
|
4298
|
+
rxOnWhenIdle: 0,
|
|
4299
|
+
securityCapability: 0,
|
|
4300
|
+
},
|
|
4301
|
+
},
|
|
4302
|
+
]);
|
|
4303
|
+
|
|
4304
|
+
// doesn't reset on announce
|
|
4305
|
+
expect(device.checkinInterval).toStrictEqual(newCheckinInterval / 4);
|
|
4306
|
+
expect(device.pendingRequestTimeout).toStrictEqual((newCheckinInterval / 4) * 1000);
|
|
4307
|
+
|
|
4308
|
+
device.removeFromDatabase();
|
|
4309
|
+
|
|
4310
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 174, ieeeAddr: "0x174"});
|
|
4311
|
+
|
|
4312
|
+
// resets on join
|
|
4313
|
+
expect(device.checkinInterval).toStrictEqual(DEFAULT_184_CHECKIN_INTERVAL / 4);
|
|
4314
|
+
expect(device.pendingRequestTimeout).toStrictEqual((DEFAULT_184_CHECKIN_INTERVAL / 4) * 1000);
|
|
4098
4315
|
});
|
|
4099
4316
|
|
|
4100
4317
|
it("Poll control unsupported", async () => {
|
|
@@ -4437,11 +4654,11 @@ describe("Controller", () => {
|
|
|
4437
4654
|
mocksendZclFrameToEndpoint.mockClear();
|
|
4438
4655
|
|
|
4439
4656
|
// @ts-expect-error private
|
|
4440
|
-
endpoint._configuredReportings = [{cluster:
|
|
4657
|
+
endpoint._configuredReportings = [{cluster: 0xfc57, attrId: 0x0005, minRepIntval: 60, maxRepIntval: 900, repChange: 1}];
|
|
4441
4658
|
|
|
4442
|
-
await endpoint.configureReporting("
|
|
4659
|
+
await endpoint.configureReporting("manuSpecificAmazonWWAH", [
|
|
4443
4660
|
{
|
|
4444
|
-
attribute: "
|
|
4661
|
+
attribute: "macRetryCount",
|
|
4445
4662
|
minimumReportInterval: 1,
|
|
4446
4663
|
maximumReportInterval: 10,
|
|
4447
4664
|
reportableChange: 1,
|
|
@@ -4449,8 +4666,8 @@ describe("Controller", () => {
|
|
|
4449
4666
|
]);
|
|
4450
4667
|
|
|
4451
4668
|
expect(endpoint.configuredReportings.length).toBe(1);
|
|
4452
|
-
expect(endpoint.configuredReportings[0].attribute.name).toBe("
|
|
4453
|
-
expect(endpoint.configuredReportings[0].cluster.name).toBe("
|
|
4669
|
+
expect(endpoint.configuredReportings[0].attribute.name).toBe("macRetryCount");
|
|
4670
|
+
expect(endpoint.configuredReportings[0].cluster.name).toBe("manuSpecificAmazonWWAH");
|
|
4454
4671
|
});
|
|
4455
4672
|
|
|
4456
4673
|
it("Endpoint configure reporting for manufacturer specific attribute", async () => {
|
|
@@ -4459,9 +4676,10 @@ describe("Controller", () => {
|
|
|
4459
4676
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
4460
4677
|
// @ts-expect-error private
|
|
4461
4678
|
device._manufacturerID = 4641;
|
|
4679
|
+
device.addCustomCluster("hvacThermostat", CUSTOM_CLUSTERS.hvacThermostat);
|
|
4462
4680
|
const endpoint = device.getEndpoint(1)!;
|
|
4463
4681
|
mocksendZclFrameToEndpoint.mockClear();
|
|
4464
|
-
await endpoint.configureReporting(
|
|
4682
|
+
await endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>(
|
|
4465
4683
|
"hvacThermostat",
|
|
4466
4684
|
[
|
|
4467
4685
|
{
|
|
@@ -4478,7 +4696,7 @@ describe("Controller", () => {
|
|
|
4478
4696
|
expect(call[0]).toBe("0x129");
|
|
4479
4697
|
expect(call[1]).toBe(129);
|
|
4480
4698
|
expect(call[2]).toBe(1);
|
|
4481
|
-
expect(deepClone(call[3])).
|
|
4699
|
+
expect(deepClone(call[3])).toMatchObject(
|
|
4482
4700
|
deepClone(
|
|
4483
4701
|
Zcl.Frame.create(
|
|
4484
4702
|
Zcl.FrameType.GLOBAL,
|
|
@@ -4489,12 +4707,13 @@ describe("Controller", () => {
|
|
|
4489
4707
|
"configReport",
|
|
4490
4708
|
513,
|
|
4491
4709
|
[{attrId: 16384, dataType: 48, direction: 0, maxRepIntval: 10, minRepIntval: 1, repChange: 1}],
|
|
4492
|
-
|
|
4710
|
+
CUSTOM_CLUSTERS,
|
|
4493
4711
|
),
|
|
4494
4712
|
),
|
|
4495
4713
|
);
|
|
4496
4714
|
|
|
4497
4715
|
expect(endpoint.configuredReportings.length).toBe(1);
|
|
4716
|
+
|
|
4498
4717
|
expect({...endpoint.configuredReportings[0], cluster: undefined}).toStrictEqual({
|
|
4499
4718
|
attribute: expect.objectContaining({ID: 16384, type: 48, manufacturerCode: 4641, name: "viessmannWindowOpenInternal"}),
|
|
4500
4719
|
minimumReportInterval: 1,
|
|
@@ -4510,9 +4729,10 @@ describe("Controller", () => {
|
|
|
4510
4729
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
4511
4730
|
// @ts-expect-error private
|
|
4512
4731
|
device._manufacturerID = Zcl.ManufacturerCode.VIESSMANN_ELEKTRONIK_GMBH;
|
|
4732
|
+
device.addCustomCluster("hvacThermostat", CUSTOM_CLUSTERS.hvacThermostat);
|
|
4513
4733
|
const endpoint = device.getEndpoint(1)!;
|
|
4514
4734
|
mocksendZclFrameToEndpoint.mockClear();
|
|
4515
|
-
await endpoint.configureReporting("hvacThermostat", [
|
|
4735
|
+
await endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", [
|
|
4516
4736
|
{
|
|
4517
4737
|
attribute: "viessmannWindowOpenInternal",
|
|
4518
4738
|
minimumReportInterval: 1,
|
|
@@ -4525,7 +4745,7 @@ describe("Controller", () => {
|
|
|
4525
4745
|
expect(call[0]).toBe("0x129");
|
|
4526
4746
|
expect(call[1]).toBe(129);
|
|
4527
4747
|
expect(call[2]).toBe(1);
|
|
4528
|
-
expect(deepClone(call[3])).
|
|
4748
|
+
expect(deepClone(call[3])).toMatchObject(
|
|
4529
4749
|
deepClone(
|
|
4530
4750
|
Zcl.Frame.create(
|
|
4531
4751
|
Zcl.FrameType.GLOBAL,
|
|
@@ -4565,7 +4785,7 @@ describe("Controller", () => {
|
|
|
4565
4785
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
4566
4786
|
{
|
|
4567
4787
|
cluster: deepClone(genPowerCfg),
|
|
4568
|
-
attribute:
|
|
4788
|
+
attribute: Zcl.Utils.getClusterAttribute(genPowerCfg, "mainsFrequency", undefined),
|
|
4569
4789
|
minimumReportInterval: 1,
|
|
4570
4790
|
maximumReportInterval: 10,
|
|
4571
4791
|
reportableChange: 1,
|
|
@@ -4578,7 +4798,7 @@ describe("Controller", () => {
|
|
|
4578
4798
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
4579
4799
|
{
|
|
4580
4800
|
cluster: deepClone(genPowerCfg),
|
|
4581
|
-
attribute:
|
|
4801
|
+
attribute: Zcl.Utils.getClusterAttribute(genPowerCfg, "mainsFrequency", undefined),
|
|
4582
4802
|
minimumReportInterval: 3,
|
|
4583
4803
|
maximumReportInterval: 100,
|
|
4584
4804
|
reportableChange: 2,
|
|
@@ -4591,14 +4811,14 @@ describe("Controller", () => {
|
|
|
4591
4811
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
4592
4812
|
{
|
|
4593
4813
|
cluster: deepClone(genPowerCfg),
|
|
4594
|
-
attribute:
|
|
4814
|
+
attribute: Zcl.Utils.getClusterAttribute(genPowerCfg, "mainsFrequency", undefined),
|
|
4595
4815
|
minimumReportInterval: 3,
|
|
4596
4816
|
maximumReportInterval: 100,
|
|
4597
4817
|
reportableChange: 2,
|
|
4598
4818
|
},
|
|
4599
4819
|
{
|
|
4600
4820
|
cluster: deepClone(msOccupancySensing),
|
|
4601
|
-
attribute:
|
|
4821
|
+
attribute: Zcl.Utils.getClusterAttribute(msOccupancySensing, "occupancy", undefined),
|
|
4602
4822
|
minimumReportInterval: 3,
|
|
4603
4823
|
maximumReportInterval: 100,
|
|
4604
4824
|
reportableChange: 2,
|
|
@@ -4611,7 +4831,7 @@ describe("Controller", () => {
|
|
|
4611
4831
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
4612
4832
|
{
|
|
4613
4833
|
cluster: deepClone(genPowerCfg),
|
|
4614
|
-
attribute:
|
|
4834
|
+
attribute: Zcl.Utils.getClusterAttribute(genPowerCfg, "mainsFrequency", undefined),
|
|
4615
4835
|
minimumReportInterval: 3,
|
|
4616
4836
|
maximumReportInterval: 100,
|
|
4617
4837
|
reportableChange: 2,
|
|
@@ -4927,14 +5147,37 @@ describe("Controller", () => {
|
|
|
4927
5147
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
4928
5148
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
4929
5149
|
const endpoint = device.getEndpoint(1)!;
|
|
4930
|
-
const genBasic = Zcl.Utils.getCluster("genBasic", undefined, {});
|
|
4931
5150
|
const saveClusterAttributeReportConfigSpy = vi.spyOn(endpoint, "saveClusterAttributeReportConfig");
|
|
4932
5151
|
|
|
4933
|
-
|
|
5152
|
+
interface SchneiderGenBasic {
|
|
5153
|
+
attributes: {schneiderMeterRadioPower: number};
|
|
5154
|
+
commands: Record<string, never>;
|
|
5155
|
+
commandResponses: Record<string, never>;
|
|
5156
|
+
}
|
|
5157
|
+
|
|
5158
|
+
device.addCustomCluster("genBasic", {
|
|
5159
|
+
name: "genBasic",
|
|
5160
|
+
ID: 0x0000,
|
|
5161
|
+
attributes: {
|
|
5162
|
+
schneiderMeterRadioPower: {
|
|
5163
|
+
name: "schneiderMeterRadioPower",
|
|
5164
|
+
ID: 0xe200,
|
|
5165
|
+
type: Zcl.DataType.INT8,
|
|
5166
|
+
manufacturerCode: Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC,
|
|
5167
|
+
write: true,
|
|
5168
|
+
min: -128,
|
|
5169
|
+
max: 127,
|
|
5170
|
+
},
|
|
5171
|
+
},
|
|
5172
|
+
commands: {},
|
|
5173
|
+
commandsResponse: {},
|
|
5174
|
+
});
|
|
5175
|
+
|
|
5176
|
+
endpoint.saveClusterAttributeReportConfig(Zcl.Clusters.genBasic.ID, Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC, [
|
|
4934
5177
|
{
|
|
4935
5178
|
status: Zcl.Status.SUCCESS,
|
|
4936
5179
|
direction: Zcl.Direction.CLIENT_TO_SERVER,
|
|
4937
|
-
attrId:
|
|
5180
|
+
attrId: 0xe200,
|
|
4938
5181
|
dataType: Zcl.DataType.INT8,
|
|
4939
5182
|
minRepIntval: 80,
|
|
4940
5183
|
maxRepIntval: 300,
|
|
@@ -4949,7 +5192,7 @@ describe("Controller", () => {
|
|
|
4949
5192
|
{
|
|
4950
5193
|
status: Zcl.Status.SUCCESS,
|
|
4951
5194
|
direction: Zcl.Direction.CLIENT_TO_SERVER,
|
|
4952
|
-
attrId:
|
|
5195
|
+
attrId: 0xe200,
|
|
4953
5196
|
dataType: Zcl.DataType.INT8,
|
|
4954
5197
|
minRepIntval: 15,
|
|
4955
5198
|
maxRepIntval: 213,
|
|
@@ -4972,9 +5215,16 @@ describe("Controller", () => {
|
|
|
4972
5215
|
|
|
4973
5216
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
4974
5217
|
{
|
|
4975
|
-
cluster:
|
|
5218
|
+
cluster: expect.objectContaining({
|
|
5219
|
+
ID: 0x0000,
|
|
5220
|
+
name: "genBasic",
|
|
5221
|
+
attributes: expect.objectContaining({
|
|
5222
|
+
zclVersion: expect.objectContaining({ID: 0x0000}),
|
|
5223
|
+
schneiderMeterRadioPower: expect.objectContaining({ID: 0xe200}),
|
|
5224
|
+
}),
|
|
5225
|
+
}),
|
|
4976
5226
|
attribute: expect.objectContaining({
|
|
4977
|
-
ID:
|
|
5227
|
+
ID: 0xe200,
|
|
4978
5228
|
name: "schneiderMeterRadioPower",
|
|
4979
5229
|
type: Zcl.DataType.INT8,
|
|
4980
5230
|
manufacturerCode: Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC,
|
|
@@ -4985,7 +5235,7 @@ describe("Controller", () => {
|
|
|
4985
5235
|
},
|
|
4986
5236
|
]);
|
|
4987
5237
|
|
|
4988
|
-
await endpoint.readReportingConfig("genBasic", [{attribute: "schneiderMeterRadioPower"}], {
|
|
5238
|
+
await endpoint.readReportingConfig<"genBasic", SchneiderGenBasic>("genBasic", [{attribute: "schneiderMeterRadioPower"}], {
|
|
4989
5239
|
manufacturerCode: Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC,
|
|
4990
5240
|
});
|
|
4991
5241
|
|
|
@@ -5002,9 +5252,9 @@ describe("Controller", () => {
|
|
|
5002
5252
|
Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC,
|
|
5003
5253
|
9,
|
|
5004
5254
|
"readReportConfig",
|
|
5005
|
-
genBasic.ID,
|
|
5006
|
-
[{direction: Zcl.Direction.CLIENT_TO_SERVER, attrId:
|
|
5007
|
-
|
|
5255
|
+
Zcl.Clusters.genBasic.ID,
|
|
5256
|
+
[{direction: Zcl.Direction.CLIENT_TO_SERVER, attrId: 0xe200}],
|
|
5257
|
+
device.customClusters,
|
|
5008
5258
|
),
|
|
5009
5259
|
),
|
|
5010
5260
|
);
|
|
@@ -5012,9 +5262,16 @@ describe("Controller", () => {
|
|
|
5012
5262
|
|
|
5013
5263
|
expect(deepClone(endpoint.configuredReportings)).toStrictEqual([
|
|
5014
5264
|
{
|
|
5015
|
-
cluster:
|
|
5265
|
+
cluster: expect.objectContaining({
|
|
5266
|
+
ID: 0x0000,
|
|
5267
|
+
name: "genBasic",
|
|
5268
|
+
attributes: expect.objectContaining({
|
|
5269
|
+
zclVersion: expect.objectContaining({ID: 0x0000}),
|
|
5270
|
+
schneiderMeterRadioPower: expect.objectContaining({ID: 0xe200}),
|
|
5271
|
+
}),
|
|
5272
|
+
}),
|
|
5016
5273
|
attribute: expect.objectContaining({
|
|
5017
|
-
ID:
|
|
5274
|
+
ID: 0xe200,
|
|
5018
5275
|
name: "schneiderMeterRadioPower",
|
|
5019
5276
|
type: Zcl.DataType.INT8,
|
|
5020
5277
|
manufacturerCode: Zcl.ManufacturerCode.SCHNEIDER_ELECTRIC,
|
|
@@ -5203,10 +5460,11 @@ describe("Controller", () => {
|
|
|
5203
5460
|
const endpoint = device.getEndpoint(1)!;
|
|
5204
5461
|
mocksendZclFrameToEndpoint.mockClear();
|
|
5205
5462
|
device.addCustomCluster("manuSpecificAssaDoorLock", {
|
|
5463
|
+
name: "manuSpecificAssaDoorLock",
|
|
5206
5464
|
ID: 0xfc00,
|
|
5207
5465
|
attributes: {},
|
|
5208
5466
|
commands: {
|
|
5209
|
-
getBatteryLevel: {ID: 0x12, parameters: []},
|
|
5467
|
+
getBatteryLevel: {name: "getBatteryLevel", ID: 0x12, parameters: []},
|
|
5210
5468
|
},
|
|
5211
5469
|
commandsResponse: {},
|
|
5212
5470
|
});
|
|
@@ -5223,6 +5481,7 @@ describe("Controller", () => {
|
|
|
5223
5481
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
5224
5482
|
const endpoint = device.getEndpoint(1)!;
|
|
5225
5483
|
mocksendZclFrameToEndpoint.mockClear();
|
|
5484
|
+
device.addCustomCluster("lightingColorCtrl", CUSTOM_CLUSTERS.lightingColorCtrl);
|
|
5226
5485
|
await endpoint.command("lightingColorCtrl", "tuyaMoveToHueAndSaturationBrightness", {hue: 1, saturation: 1, transtime: 0, brightness: 22});
|
|
5227
5486
|
expect(mocksendZclFrameToEndpoint.mock.calls[0][0]).toBe("0x129");
|
|
5228
5487
|
expect(mocksendZclFrameToEndpoint.mock.calls[0][1]).toBe(129);
|
|
@@ -5468,14 +5727,15 @@ describe("Controller", () => {
|
|
|
5468
5727
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
5469
5728
|
// @ts-expect-error private
|
|
5470
5729
|
device._manufacturerID = Zcl.ManufacturerCode.VIESSMANN_ELEKTRONIK_GMBH;
|
|
5730
|
+
device.addCustomCluster("hvacThermostat", CUSTOM_CLUSTERS.hvacThermostat);
|
|
5471
5731
|
const endpoint = device.getEndpoint(1)!;
|
|
5472
|
-
await endpoint.write("hvacThermostat", {viessmannWindowOpenInternal: 1});
|
|
5732
|
+
await endpoint.write<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", {viessmannWindowOpenInternal: 1});
|
|
5473
5733
|
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
5474
5734
|
const call = mocksendZclFrameToEndpoint.mock.calls[0];
|
|
5475
5735
|
expect(call[0]).toBe("0x129");
|
|
5476
5736
|
expect(call[1]).toBe(129);
|
|
5477
5737
|
expect(call[2]).toBe(1);
|
|
5478
|
-
expect(deepClone(call[3])).
|
|
5738
|
+
expect(deepClone(call[3])).toMatchObject(
|
|
5479
5739
|
deepClone(
|
|
5480
5740
|
Zcl.Frame.create(
|
|
5481
5741
|
Zcl.FrameType.GLOBAL,
|
|
@@ -5486,7 +5746,7 @@ describe("Controller", () => {
|
|
|
5486
5746
|
"write",
|
|
5487
5747
|
513,
|
|
5488
5748
|
[{attrId: 16384, attrData: 1, dataType: 48}],
|
|
5489
|
-
|
|
5749
|
+
CUSTOM_CLUSTERS,
|
|
5490
5750
|
),
|
|
5491
5751
|
),
|
|
5492
5752
|
);
|
|
@@ -5746,17 +6006,29 @@ describe("Controller", () => {
|
|
|
5746
6006
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
5747
6007
|
mocksendZclFrameToEndpoint.mockClear();
|
|
5748
6008
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
5749
|
-
|
|
5750
|
-
device.
|
|
6009
|
+
readManufacturerCode = Zcl.ManufacturerCode.VIESSMANN_ELEKTRONIK_GMBH;
|
|
6010
|
+
device.addCustomCluster("hvacThermostat", CUSTOM_CLUSTERS.hvacThermostat);
|
|
5751
6011
|
const endpoint = device.getEndpoint(1)!;
|
|
5752
|
-
await endpoint.read("hvacThermostat", ["viessmannWindowOpenInternal"]);
|
|
6012
|
+
await endpoint.read<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", ["viessmannWindowOpenInternal"]);
|
|
5753
6013
|
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
5754
6014
|
const call = mocksendZclFrameToEndpoint.mock.calls[0];
|
|
5755
6015
|
expect(call[0]).toBe("0x129");
|
|
5756
6016
|
expect(call[1]).toBe(129);
|
|
5757
6017
|
expect(call[2]).toBe(1);
|
|
5758
|
-
expect(deepClone(call[3])).
|
|
5759
|
-
deepClone(
|
|
6018
|
+
expect(deepClone(call[3])).toMatchObject(
|
|
6019
|
+
deepClone(
|
|
6020
|
+
Zcl.Frame.create(
|
|
6021
|
+
Zcl.FrameType.GLOBAL,
|
|
6022
|
+
Zcl.Direction.CLIENT_TO_SERVER,
|
|
6023
|
+
true,
|
|
6024
|
+
4641,
|
|
6025
|
+
9,
|
|
6026
|
+
"read",
|
|
6027
|
+
513,
|
|
6028
|
+
[{attrId: 16384}],
|
|
6029
|
+
CUSTOM_CLUSTERS,
|
|
6030
|
+
),
|
|
6031
|
+
),
|
|
5760
6032
|
);
|
|
5761
6033
|
expect(call[4]).toBe(10000);
|
|
5762
6034
|
});
|
|
@@ -5914,7 +6186,7 @@ describe("Controller", () => {
|
|
|
5914
6186
|
expect(Array.from(group6.members)).toStrictEqual([device2.getEndpoint(1)]);
|
|
5915
6187
|
expect(Array.from(group7.members)).toStrictEqual([device2.getEndpoint(1)]);
|
|
5916
6188
|
expect(deepClone(call[3])).toStrictEqual(
|
|
5917
|
-
deepClone(Zcl.Frame.create(Zcl.FrameType.SPECIFIC, Zcl.Direction.CLIENT_TO_SERVER, true, undefined,
|
|
6189
|
+
deepClone(Zcl.Frame.create(Zcl.FrameType.SPECIFIC, Zcl.Direction.CLIENT_TO_SERVER, true, undefined, 22, "removeAll", 4, {}, {})),
|
|
5918
6190
|
);
|
|
5919
6191
|
});
|
|
5920
6192
|
|
|
@@ -7323,10 +7595,11 @@ describe("Controller", () => {
|
|
|
7323
7595
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
7324
7596
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
7325
7597
|
device.addCustomCluster("myCustomCluster", {
|
|
7598
|
+
name: "myCustomCluster",
|
|
7326
7599
|
ID: 9123,
|
|
7327
7600
|
commands: {},
|
|
7328
7601
|
commandsResponse: {},
|
|
7329
|
-
attributes: {superAttribute: {ID: 0, type: Zcl.DataType.UINT8}},
|
|
7602
|
+
attributes: {superAttribute: {name: "superAttribute", ID: 0, type: Zcl.DataType.UINT8}},
|
|
7330
7603
|
});
|
|
7331
7604
|
const buffer = Buffer.from([24, 169, 99, 0, 1, 24, 3, 0, 0, 24, 1]);
|
|
7332
7605
|
const header = Zcl.Header.fromBuffer(buffer);
|
|
@@ -8346,13 +8619,6 @@ describe("Controller", () => {
|
|
|
8346
8619
|
mocksendZclFrameToEndpoint.mockClear();
|
|
8347
8620
|
mocksendZclFrameToEndpoint.mockReturnValueOnce(null);
|
|
8348
8621
|
|
|
8349
|
-
// onZclData is called via mockAdapterEvents, but we need to wait until it has finished
|
|
8350
|
-
const origOnZclData = device.onZclData;
|
|
8351
|
-
device.onZclData = (a, b, c) => {
|
|
8352
|
-
const f = origOnZclData.call(device, a, b, c);
|
|
8353
|
-
vi.advanceTimersByTime(10);
|
|
8354
|
-
return f;
|
|
8355
|
-
};
|
|
8356
8622
|
const nextTick = new Promise(process.nextTick);
|
|
8357
8623
|
|
|
8358
8624
|
const result = endpoint.write("genOnOff", {onTime: 1}, {disableResponse: true, sendPolicy: "bulk"});
|
|
@@ -8424,6 +8690,128 @@ describe("Controller", () => {
|
|
|
8424
8690
|
expect(fastpollstop[3].payload).toStrictEqual({});
|
|
8425
8691
|
});
|
|
8426
8692
|
|
|
8693
|
+
it("Send to device with pendingRequestTimeout > 0, retry on timeout error", async () => {
|
|
8694
|
+
await controller.start();
|
|
8695
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
8696
|
+
const frame = Zcl.Frame.create(
|
|
8697
|
+
Zcl.FrameType.GLOBAL,
|
|
8698
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
8699
|
+
true,
|
|
8700
|
+
undefined,
|
|
8701
|
+
1,
|
|
8702
|
+
"report",
|
|
8703
|
+
"genPowerCfg",
|
|
8704
|
+
[{attrId: 33, dataType: 32, attrData: 84}],
|
|
8705
|
+
{},
|
|
8706
|
+
);
|
|
8707
|
+
const data = {
|
|
8708
|
+
wasBroadcast: false,
|
|
8709
|
+
address: "0x129",
|
|
8710
|
+
clusterID: frame.cluster.ID,
|
|
8711
|
+
data: frame.toBuffer(),
|
|
8712
|
+
header: frame.header,
|
|
8713
|
+
endpoint: 1,
|
|
8714
|
+
linkquality: 50,
|
|
8715
|
+
groupID: 0,
|
|
8716
|
+
};
|
|
8717
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
8718
|
+
const endpoint = device.getEndpoint(1)!;
|
|
8719
|
+
const group = controller.createGroup(1);
|
|
8720
|
+
device.checkinInterval = 3_600;
|
|
8721
|
+
|
|
8722
|
+
expect(device.pendingRequestTimeout).toStrictEqual(3_600_000);
|
|
8723
|
+
|
|
8724
|
+
mocksendZclFrameToEndpoint.mockClear();
|
|
8725
|
+
mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {
|
|
8726
|
+
await vi.advanceTimersByTimeAsync(10_000);
|
|
8727
|
+
throw new Error("timeout");
|
|
8728
|
+
});
|
|
8729
|
+
|
|
8730
|
+
const p = endpoint.addToGroup(group);
|
|
8731
|
+
|
|
8732
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
8733
|
+
|
|
8734
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
8735
|
+
await mockAdapterEvents.zclPayload(data);
|
|
8736
|
+
|
|
8737
|
+
await expect(p).resolves.toStrictEqual(undefined);
|
|
8738
|
+
|
|
8739
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(2);
|
|
8740
|
+
});
|
|
8741
|
+
|
|
8742
|
+
it("Send to device with pendingRequestTimeout > 0, fails on non-timeout error", async () => {
|
|
8743
|
+
await controller.start();
|
|
8744
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
8745
|
+
const frame = Zcl.Frame.create(
|
|
8746
|
+
Zcl.FrameType.GLOBAL,
|
|
8747
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
8748
|
+
true,
|
|
8749
|
+
undefined,
|
|
8750
|
+
1,
|
|
8751
|
+
"report",
|
|
8752
|
+
"genPowerCfg",
|
|
8753
|
+
[{attrId: 33, dataType: 32, attrData: 84}],
|
|
8754
|
+
{},
|
|
8755
|
+
);
|
|
8756
|
+
const data = {
|
|
8757
|
+
wasBroadcast: false,
|
|
8758
|
+
address: "0x129",
|
|
8759
|
+
clusterID: frame.cluster.ID,
|
|
8760
|
+
data: frame.toBuffer(),
|
|
8761
|
+
header: frame.header,
|
|
8762
|
+
endpoint: 1,
|
|
8763
|
+
linkquality: 50,
|
|
8764
|
+
groupID: 0,
|
|
8765
|
+
};
|
|
8766
|
+
const failureFrame = Zcl.Frame.create(
|
|
8767
|
+
Zcl.FrameType.GLOBAL,
|
|
8768
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
8769
|
+
true,
|
|
8770
|
+
undefined,
|
|
8771
|
+
2,
|
|
8772
|
+
"defaultRsp",
|
|
8773
|
+
"genGroups",
|
|
8774
|
+
{cmdId: 0, statusCode: Zcl.Status.UNSUPPORTED_CLUSTER},
|
|
8775
|
+
{},
|
|
8776
|
+
);
|
|
8777
|
+
const failureData = {
|
|
8778
|
+
wasBroadcast: false,
|
|
8779
|
+
address: "0x129",
|
|
8780
|
+
clusterID: failureFrame.cluster.ID,
|
|
8781
|
+
data: failureFrame.toBuffer(),
|
|
8782
|
+
header: failureFrame.header,
|
|
8783
|
+
endpoint: 1,
|
|
8784
|
+
linkquality: 50,
|
|
8785
|
+
groupID: 0,
|
|
8786
|
+
};
|
|
8787
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
8788
|
+
const endpoint = device.getEndpoint(1)!;
|
|
8789
|
+
const group = controller.createGroup(1);
|
|
8790
|
+
device.checkinInterval = 3_600;
|
|
8791
|
+
expect(device.pendingRequestTimeout).toStrictEqual(3_600_000);
|
|
8792
|
+
|
|
8793
|
+
mocksendZclFrameToEndpoint.mockClear();
|
|
8794
|
+
mocksendZclFrameToEndpoint.mockImplementationOnce(async () => {
|
|
8795
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
8796
|
+
await mockAdapterEvents.zclPayload(failureData);
|
|
8797
|
+
|
|
8798
|
+
return failureData;
|
|
8799
|
+
});
|
|
8800
|
+
|
|
8801
|
+
const p = endpoint.addToGroup(group);
|
|
8802
|
+
|
|
8803
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
8804
|
+
|
|
8805
|
+
await expect(p).rejects.toThrow(/UNSUPPORTED_CLUSTER/);
|
|
8806
|
+
|
|
8807
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
8808
|
+
|
|
8809
|
+
await vi.advanceTimersByTimeAsync(1000);
|
|
8810
|
+
await mockAdapterEvents.zclPayload(data);
|
|
8811
|
+
|
|
8812
|
+
expect(mocksendZclFrameToEndpoint).toHaveBeenCalledTimes(1);
|
|
8813
|
+
});
|
|
8814
|
+
|
|
8427
8815
|
it("Handle retransmitted Xiaomi messages", async () => {
|
|
8428
8816
|
await controller.start();
|
|
8429
8817
|
await mockAdapterEvents.deviceJoined({networkAddress: 175, ieeeAddr: "0x175"});
|
|
@@ -8479,111 +8867,274 @@ describe("Controller", () => {
|
|
|
8479
8867
|
expect(result.missingRouters[0].ieeeAddr).toBe("0x129");
|
|
8480
8868
|
});
|
|
8481
8869
|
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8870
|
+
describe("ZCL frame converter attributeKeyValue", () => {
|
|
8871
|
+
// ZCLFrame with manufacturer specific flag and manufacturer code defined, to generic device
|
|
8872
|
+
// ZCLFrameConverter should not modify specific frames!
|
|
8873
|
+
it("Should resolve manufacturer specific cluster attribute names on specific ZCL frames: generic target device", async () => {
|
|
8874
|
+
const buffer = Buffer.from([28, 33, 16, 13, 1, 2, 240, 0, 48, 4]);
|
|
8875
|
+
await controller.start();
|
|
8876
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
8877
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
8878
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
8488
8879
|
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8880
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
8881
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
8882
|
+
Zcl.Header.fromBuffer(buffer),
|
|
8883
|
+
buffer,
|
|
8884
|
+
CUSTOM_CLUSTERS,
|
|
8885
|
+
);
|
|
8886
|
+
await mockAdapterEvents.zclPayload({
|
|
8887
|
+
wasBroadcast: false,
|
|
8888
|
+
address: "0x129",
|
|
8889
|
+
clusterID: frame.cluster.ID,
|
|
8890
|
+
data: frame.toBuffer(),
|
|
8891
|
+
header: frame.header,
|
|
8892
|
+
endpoint: 1,
|
|
8893
|
+
linkquality: 50,
|
|
8894
|
+
groupID: 0,
|
|
8895
|
+
});
|
|
8896
|
+
expect(events.message.length).toBe(1);
|
|
8897
|
+
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8898
|
+
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8504
8899
|
});
|
|
8505
|
-
expect(events.message.length).toBe(1);
|
|
8506
|
-
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8507
|
-
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8508
|
-
});
|
|
8509
8900
|
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8901
|
+
// ZCLFrame with manufacturer specific flag and manufacturer code defined, to specific device
|
|
8902
|
+
// ZCLFrameConverter should not modify specific frames!
|
|
8903
|
+
it("Should resolve manufacturer specific cluster attribute names on specific ZCL frames: specific target device", async () => {
|
|
8904
|
+
const buffer = Buffer.from([28, 33, 16, 13, 1, 2, 240, 0, 48, 4]);
|
|
8905
|
+
await controller.start();
|
|
8906
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 177, ieeeAddr: "0x177"});
|
|
8907
|
+
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
8908
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
8909
|
+
|
|
8910
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
8911
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
8912
|
+
Zcl.Header.fromBuffer(buffer),
|
|
8913
|
+
buffer,
|
|
8914
|
+
CUSTOM_CLUSTERS,
|
|
8915
|
+
);
|
|
8916
|
+
await mockAdapterEvents.zclPayload({
|
|
8917
|
+
wasBroadcast: false,
|
|
8918
|
+
address: "0x177",
|
|
8919
|
+
clusterID: frame.cluster.ID,
|
|
8920
|
+
data: frame.toBuffer(),
|
|
8921
|
+
header: frame.header,
|
|
8922
|
+
endpoint: 1,
|
|
8923
|
+
linkquality: 50,
|
|
8924
|
+
groupID: 0,
|
|
8925
|
+
});
|
|
8926
|
+
expect(events.message.length).toBe(1);
|
|
8927
|
+
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8928
|
+
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8531
8929
|
});
|
|
8532
|
-
expect(events.message.length).toBe(1);
|
|
8533
|
-
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8534
|
-
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8535
|
-
});
|
|
8536
8930
|
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8931
|
+
// ZCLFrame without manufacturer specific flag or manufacturer code set, to generic device
|
|
8932
|
+
it("Should resolve generic cluster attribute names on generic ZCL frames: generic target device", async () => {
|
|
8933
|
+
const buffer = Buffer.from([24, 242, 10, 2, 240, 48, 4]);
|
|
8934
|
+
await controller.start();
|
|
8935
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
8936
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
8937
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
8938
|
+
|
|
8939
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
8940
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
8941
|
+
Zcl.Header.fromBuffer(buffer),
|
|
8942
|
+
buffer,
|
|
8943
|
+
CUSTOM_CLUSTERS,
|
|
8944
|
+
);
|
|
8945
|
+
await mockAdapterEvents.zclPayload({
|
|
8946
|
+
wasBroadcast: false,
|
|
8947
|
+
address: "0x129",
|
|
8948
|
+
clusterID: frame.cluster.ID,
|
|
8949
|
+
data: frame.toBuffer(),
|
|
8950
|
+
header: frame.header,
|
|
8951
|
+
endpoint: 1,
|
|
8952
|
+
linkquality: 50,
|
|
8953
|
+
groupID: 0,
|
|
8954
|
+
});
|
|
8955
|
+
expect(events.message.length).toBe(1);
|
|
8956
|
+
expect(events.message[0].data).toMatchObject({tuyaMotorReversal: 4});
|
|
8957
|
+
expect(events.message[0].data).not.toMatchObject({calibrationMode: 4});
|
|
8958
|
+
});
|
|
8959
|
+
|
|
8960
|
+
// ZCLFrame without manufacturer specific flag set or manufacturer code set, to specific device (Legrand only)
|
|
8961
|
+
it("Should resolve manufacturer specific cluster attribute names on generic ZCL frames: Legrand target device", async () => {
|
|
8962
|
+
const buffer = Buffer.from([24, 242, 10, 2, 240, 48, 4]);
|
|
8963
|
+
await controller.start();
|
|
8964
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 177, ieeeAddr: "0x177"});
|
|
8965
|
+
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
8966
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
8967
|
+
|
|
8968
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
8969
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
8970
|
+
Zcl.Header.fromBuffer(buffer),
|
|
8971
|
+
buffer,
|
|
8972
|
+
CUSTOM_CLUSTERS,
|
|
8973
|
+
);
|
|
8974
|
+
await mockAdapterEvents.zclPayload({
|
|
8975
|
+
wasBroadcast: false,
|
|
8976
|
+
address: "0x177",
|
|
8977
|
+
clusterID: frame.cluster.ID,
|
|
8978
|
+
data: frame.toBuffer(),
|
|
8979
|
+
header: frame.header,
|
|
8980
|
+
endpoint: 1,
|
|
8981
|
+
linkquality: 50,
|
|
8982
|
+
groupID: 0,
|
|
8983
|
+
});
|
|
8984
|
+
expect(events.message.length).toBe(1);
|
|
8985
|
+
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8986
|
+
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8987
|
+
});
|
|
8988
|
+
|
|
8989
|
+
it("Should drop non-success records from readRsp payloads", async () => {
|
|
8990
|
+
await controller.start();
|
|
8991
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
8992
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
8993
|
+
device.updateGenBasic({swBuildId: "2.16"});
|
|
8994
|
+
|
|
8995
|
+
const frame = Zcl.Frame.create(
|
|
8996
|
+
Zcl.FrameType.GLOBAL,
|
|
8997
|
+
Zcl.Direction.SERVER_TO_CLIENT,
|
|
8998
|
+
true,
|
|
8999
|
+
undefined,
|
|
9000
|
+
10,
|
|
9001
|
+
"readRsp",
|
|
9002
|
+
Zcl.Clusters.genBasic.ID,
|
|
9003
|
+
[
|
|
9004
|
+
{attrId: 0x0005, status: Zcl.Status.SUCCESS, dataType: Zcl.DataType.CHAR_STR, attrData: "myModelID"},
|
|
9005
|
+
{attrId: 0x4000, status: Zcl.Status.UNSUPPORTED_ATTRIBUTE},
|
|
9006
|
+
],
|
|
9007
|
+
{},
|
|
9008
|
+
);
|
|
9009
|
+
await mockAdapterEvents.zclPayload({
|
|
9010
|
+
wasBroadcast: false,
|
|
9011
|
+
address: "0x129",
|
|
9012
|
+
clusterID: frame.cluster.ID,
|
|
9013
|
+
data: frame.toBuffer(),
|
|
9014
|
+
header: frame.header,
|
|
9015
|
+
endpoint: 1,
|
|
9016
|
+
linkquality: 50,
|
|
9017
|
+
groupID: 0,
|
|
9018
|
+
});
|
|
9019
|
+
|
|
9020
|
+
expect(events.message.length).toBe(1);
|
|
9021
|
+
expect(events.message[0].data).toStrictEqual({modelId: "myModelID"});
|
|
9022
|
+
expect(device.softwareBuildID).toStrictEqual("2.16");
|
|
9023
|
+
expect(device.modelID).toStrictEqual("myModelID");
|
|
8557
9024
|
});
|
|
8558
|
-
expect(events.message.length).toBe(1);
|
|
8559
|
-
expect(events.message[0].data).toMatchObject({tuyaMotorReversal: 4});
|
|
8560
|
-
expect(events.message[0].data).not.toMatchObject({calibrationMode: 4});
|
|
8561
9025
|
});
|
|
8562
9026
|
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
9027
|
+
describe("ZCL frame converter attributeList", () => {
|
|
9028
|
+
// ZCLFrame with manufacturer specific flag and manufacturer code defined, to generic device
|
|
9029
|
+
// ZCLFrameConverter should not modify specific frames!
|
|
9030
|
+
it("Should resolve manufacturer specific cluster attribute names on specific ZCL frames: generic target device", async () => {
|
|
9031
|
+
const buffer = Buffer.from([28, 33, 16, 13, 0, 2, 240]);
|
|
9032
|
+
await controller.start();
|
|
9033
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
9034
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
9035
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
9036
|
+
|
|
9037
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
9038
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
9039
|
+
Zcl.Header.fromBuffer(buffer),
|
|
9040
|
+
buffer,
|
|
9041
|
+
{},
|
|
9042
|
+
);
|
|
9043
|
+
await mockAdapterEvents.zclPayload({
|
|
9044
|
+
wasBroadcast: false,
|
|
9045
|
+
address: "0x129",
|
|
9046
|
+
clusterID: frame.cluster.ID,
|
|
9047
|
+
data: frame.toBuffer(),
|
|
9048
|
+
header: frame.header,
|
|
9049
|
+
endpoint: 1,
|
|
9050
|
+
linkquality: 50,
|
|
9051
|
+
groupID: 0,
|
|
9052
|
+
});
|
|
9053
|
+
expect(events.message.length).toBe(1);
|
|
9054
|
+
expect(events.message[0].data).toStrictEqual(["calibrationMode"]);
|
|
9055
|
+
});
|
|
9056
|
+
|
|
9057
|
+
// ZCLFrame with manufacturer specific flag and manufacturer code defined, to specific device
|
|
9058
|
+
// ZCLFrameConverter should not modify specific frames!
|
|
9059
|
+
it("Should resolve manufacturer specific cluster attribute names on specific ZCL frames: specific target device", async () => {
|
|
9060
|
+
const buffer = Buffer.from([28, 33, 16, 13, 0, 2, 240]);
|
|
9061
|
+
await controller.start();
|
|
9062
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 177, ieeeAddr: "0x177"});
|
|
9063
|
+
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
9064
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
9065
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
9066
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
9067
|
+
Zcl.Header.fromBuffer(buffer),
|
|
9068
|
+
buffer,
|
|
9069
|
+
{},
|
|
9070
|
+
);
|
|
9071
|
+
await mockAdapterEvents.zclPayload({
|
|
9072
|
+
wasBroadcast: false,
|
|
9073
|
+
address: "0x177",
|
|
9074
|
+
clusterID: frame.cluster.ID,
|
|
9075
|
+
data: frame.toBuffer(),
|
|
9076
|
+
header: frame.header,
|
|
9077
|
+
endpoint: 1,
|
|
9078
|
+
linkquality: 50,
|
|
9079
|
+
groupID: 0,
|
|
9080
|
+
});
|
|
9081
|
+
expect(events.message.length).toBe(1);
|
|
9082
|
+
expect(events.message[0].data).toStrictEqual(["calibrationMode"]);
|
|
9083
|
+
});
|
|
9084
|
+
|
|
9085
|
+
// ZCLFrame without manufacturer specific flag or manufacturer code set, to generic device
|
|
9086
|
+
it("Should resolve generic cluster attribute names on generic ZCL frames: generic target device", async () => {
|
|
9087
|
+
const buffer = Buffer.from([24, 242, 0, 2, 240]);
|
|
9088
|
+
await controller.start();
|
|
9089
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
9090
|
+
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
9091
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
9092
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
9093
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
9094
|
+
Zcl.Header.fromBuffer(buffer),
|
|
9095
|
+
buffer,
|
|
9096
|
+
{},
|
|
9097
|
+
);
|
|
9098
|
+
await mockAdapterEvents.zclPayload({
|
|
9099
|
+
wasBroadcast: false,
|
|
9100
|
+
address: "0x129",
|
|
9101
|
+
clusterID: frame.cluster.ID,
|
|
9102
|
+
data: frame.toBuffer(),
|
|
9103
|
+
header: frame.header,
|
|
9104
|
+
endpoint: 1,
|
|
9105
|
+
linkquality: 50,
|
|
9106
|
+
groupID: 0,
|
|
9107
|
+
});
|
|
9108
|
+
expect(events.message.length).toBe(1);
|
|
9109
|
+
expect(events.message[0].data).toStrictEqual(["tuyaMotorReversal"]);
|
|
9110
|
+
});
|
|
9111
|
+
|
|
9112
|
+
// ZCLFrame without manufacturer specific flag set or manufacturer code set, to specific device (Legrand only)
|
|
9113
|
+
it("Should resolve manufacturer specific cluster attribute names on generic ZCL frames: Legrand target device", async () => {
|
|
9114
|
+
const buffer = Buffer.from([24, 242, 0, 2, 240]);
|
|
9115
|
+
await controller.start();
|
|
9116
|
+
await mockAdapterEvents.deviceJoined({networkAddress: 177, ieeeAddr: "0x177"});
|
|
9117
|
+
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
9118
|
+
device.addCustomCluster("closuresWindowCovering", CUSTOM_CLUSTERS.closuresWindowCovering);
|
|
9119
|
+
const frame = Zcl.Frame.fromBuffer(
|
|
9120
|
+
Zcl.Utils.getCluster("closuresWindowCovering", undefined, CUSTOM_CLUSTERS).ID,
|
|
9121
|
+
Zcl.Header.fromBuffer(buffer),
|
|
9122
|
+
buffer,
|
|
9123
|
+
{},
|
|
9124
|
+
);
|
|
9125
|
+
await mockAdapterEvents.zclPayload({
|
|
9126
|
+
wasBroadcast: false,
|
|
9127
|
+
address: "0x177",
|
|
9128
|
+
clusterID: frame.cluster.ID,
|
|
9129
|
+
data: frame.toBuffer(),
|
|
9130
|
+
header: frame.header,
|
|
9131
|
+
endpoint: 1,
|
|
9132
|
+
linkquality: 50,
|
|
9133
|
+
groupID: 0,
|
|
9134
|
+
});
|
|
9135
|
+
expect(events.message.length).toBe(1);
|
|
9136
|
+
expect(events.message[0].data).toStrictEqual(["calibrationMode"]);
|
|
8583
9137
|
});
|
|
8584
|
-
expect(events.message.length).toBe(1);
|
|
8585
|
-
expect(events.message[0].data).toMatchObject({calibrationMode: 4});
|
|
8586
|
-
expect(events.message[0].data).not.toMatchObject({tuyaMotorReversal: 4});
|
|
8587
9138
|
});
|
|
8588
9139
|
|
|
8589
9140
|
it("zclCommand", async () => {
|
|
@@ -9347,10 +9898,11 @@ describe("Controller", () => {
|
|
|
9347
9898
|
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
9348
9899
|
|
|
9349
9900
|
device.addCustomCluster("manuHerdsman", {
|
|
9901
|
+
name: "manuHerdsman",
|
|
9350
9902
|
ID: 64513,
|
|
9351
9903
|
commands: {},
|
|
9352
9904
|
commandsResponse: {},
|
|
9353
|
-
attributes: {customAttr: {ID: 0, type: Zcl.DataType.UINT8, write: true}},
|
|
9905
|
+
attributes: {customAttr: {name: "customAttr", ID: 0, type: Zcl.DataType.UINT8, write: true}},
|
|
9354
9906
|
});
|
|
9355
9907
|
|
|
9356
9908
|
const group = controller.createGroup(34);
|
|
@@ -9402,20 +9954,21 @@ describe("Controller", () => {
|
|
|
9402
9954
|
|
|
9403
9955
|
const device2 = controller.getDeviceByIeeeAddr("0x178")!;
|
|
9404
9956
|
device2.addCustomCluster("manuHerdsman", {
|
|
9957
|
+
name: "manuHerdsman",
|
|
9405
9958
|
ID: 64513,
|
|
9406
9959
|
commands: {},
|
|
9407
9960
|
commandsResponse: {},
|
|
9408
|
-
attributes: {customAttr: {ID: 0, type: Zcl.DataType.UINT8}},
|
|
9961
|
+
attributes: {customAttr: {name: "customAttr", ID: 0, type: Zcl.DataType.UINT8}},
|
|
9409
9962
|
});
|
|
9410
9963
|
group.addMember(device2.getEndpoint(2)!);
|
|
9411
9964
|
|
|
9412
9965
|
await expect(async () => {
|
|
9413
9966
|
await group.write("manuHerdsman", {customAttr: 14}, {});
|
|
9414
|
-
}).rejects.toThrow(new
|
|
9967
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9415
9968
|
|
|
9416
9969
|
await expect(async () => {
|
|
9417
9970
|
await group.read("manuHerdsman", ["customAttr"], {});
|
|
9418
|
-
}).rejects.toThrow(new
|
|
9971
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9419
9972
|
|
|
9420
9973
|
await group.write<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", {customAttr: 14}, {direction: Zcl.Direction.SERVER_TO_CLIENT});
|
|
9421
9974
|
await group.read<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", ["customAttr"], {direction: Zcl.Direction.SERVER_TO_CLIENT});
|
|
@@ -9441,20 +9994,21 @@ describe("Controller", () => {
|
|
|
9441
9994
|
|
|
9442
9995
|
const device3 = controller.getDeviceByIeeeAddr("0x176")!;
|
|
9443
9996
|
device3.addCustomCluster("manuHerdsman", {
|
|
9997
|
+
name: "manuHerdsman",
|
|
9444
9998
|
ID: 64513,
|
|
9445
9999
|
commands: {},
|
|
9446
10000
|
commandsResponse: {},
|
|
9447
|
-
attributes: {customAttr: {ID: 0, type: Zcl.DataType.UINT8}},
|
|
10001
|
+
attributes: {customAttr: {name: "customAttr", ID: 0, type: Zcl.DataType.UINT8}},
|
|
9448
10002
|
});
|
|
9449
10003
|
group.addMember(device3.getEndpoint(1)!);
|
|
9450
10004
|
|
|
9451
10005
|
await expect(async () => {
|
|
9452
10006
|
await group.write("manuHerdsman", {customAttr: 56}, {});
|
|
9453
|
-
}).rejects.toThrow(new
|
|
10007
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9454
10008
|
|
|
9455
10009
|
await expect(async () => {
|
|
9456
10010
|
await group.read("manuHerdsman", ["customAttr"], {});
|
|
9457
|
-
}).rejects.toThrow(new
|
|
10011
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9458
10012
|
});
|
|
9459
10013
|
|
|
9460
10014
|
it("does not read/write to group with non-common custom clusters", async () => {
|
|
@@ -9464,10 +10018,11 @@ describe("Controller", () => {
|
|
|
9464
10018
|
const device = controller.getDeviceByIeeeAddr("0x177")!;
|
|
9465
10019
|
|
|
9466
10020
|
device.addCustomCluster("manuHerdsman", {
|
|
10021
|
+
name: "manuHerdsman",
|
|
9467
10022
|
ID: 64513,
|
|
9468
10023
|
commands: {},
|
|
9469
10024
|
commandsResponse: {},
|
|
9470
|
-
attributes: {customAttr: {ID: 0, type: Zcl.DataType.UINT8}},
|
|
10025
|
+
attributes: {customAttr: {name: "customAttr", ID: 0, type: Zcl.DataType.UINT8}},
|
|
9471
10026
|
});
|
|
9472
10027
|
|
|
9473
10028
|
const group = controller.createGroup(34);
|
|
@@ -9481,11 +10036,11 @@ describe("Controller", () => {
|
|
|
9481
10036
|
|
|
9482
10037
|
await expect(async () => {
|
|
9483
10038
|
await group.write("manuHerdsman", {customAttr: 34}, {});
|
|
9484
|
-
}).rejects.toThrow(new
|
|
10039
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9485
10040
|
|
|
9486
10041
|
await expect(async () => {
|
|
9487
10042
|
await group.read("manuHerdsman", ["customAttr"], {});
|
|
9488
|
-
}).rejects.toThrow(new
|
|
10043
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuHerdsman"));
|
|
9489
10044
|
});
|
|
9490
10045
|
|
|
9491
10046
|
it("sends & receives command to group with custom cluster when common to all members", async () => {
|
|
@@ -9495,12 +10050,14 @@ describe("Controller", () => {
|
|
|
9495
10050
|
const device = controller.getDeviceByIeeeAddr("0x179")!;
|
|
9496
10051
|
|
|
9497
10052
|
device.addCustomCluster("manuSpecificInovelli", {
|
|
10053
|
+
name: "manuSpecificInovelli",
|
|
9498
10054
|
ID: 64561,
|
|
9499
10055
|
manufacturerCode: Zcl.ManufacturerCode.V_MARK_ENTERPRISES_INC,
|
|
9500
10056
|
// omitted for brevity (unused here)
|
|
9501
10057
|
attributes: {},
|
|
9502
10058
|
commands: {
|
|
9503
10059
|
ledEffect: {
|
|
10060
|
+
name: "ledEffect",
|
|
9504
10061
|
ID: 1,
|
|
9505
10062
|
parameters: [
|
|
9506
10063
|
{name: "effect", type: Zcl.DataType.UINT8},
|
|
@@ -9510,6 +10067,7 @@ describe("Controller", () => {
|
|
|
9510
10067
|
],
|
|
9511
10068
|
},
|
|
9512
10069
|
individualLedEffect: {
|
|
10070
|
+
name: "individualLedEffect",
|
|
9513
10071
|
ID: 3,
|
|
9514
10072
|
parameters: [
|
|
9515
10073
|
{name: "led", type: Zcl.DataType.UINT8},
|
|
@@ -9521,10 +10079,7 @@ describe("Controller", () => {
|
|
|
9521
10079
|
},
|
|
9522
10080
|
},
|
|
9523
10081
|
commandsResponse: {
|
|
9524
|
-
bogus: {
|
|
9525
|
-
ID: 1,
|
|
9526
|
-
parameters: [{name: "xyz", type: Zcl.DataType.UINT8}],
|
|
9527
|
-
},
|
|
10082
|
+
bogus: {name: "bogus", ID: 1, parameters: [{name: "xyz", type: Zcl.DataType.UINT8}]},
|
|
9528
10083
|
},
|
|
9529
10084
|
});
|
|
9530
10085
|
|
|
@@ -9617,7 +10172,7 @@ describe("Controller", () => {
|
|
|
9617
10172
|
level: 200,
|
|
9618
10173
|
duration: 15,
|
|
9619
10174
|
});
|
|
9620
|
-
}).rejects.toThrow(new
|
|
10175
|
+
}).rejects.toThrow(new Zcl.StatusError(Zcl.Status.UNSUPPORTED_CLUSTER, "manuSpecificInovelli"));
|
|
9621
10176
|
});
|
|
9622
10177
|
|
|
9623
10178
|
it("Updates a device genBasic properties", async () => {
|
|
@@ -9863,10 +10418,12 @@ describe("Controller", () => {
|
|
|
9863
10418
|
|
|
9864
10419
|
const myCustomClusters: CustomClusters = {
|
|
9865
10420
|
zhSpe: {
|
|
10421
|
+
name: "zhSpe",
|
|
9866
10422
|
ID: 0xffc1,
|
|
9867
10423
|
attributes: {},
|
|
9868
10424
|
commands: {
|
|
9869
10425
|
readMe: {
|
|
10426
|
+
name: "readMe",
|
|
9870
10427
|
ID: 0,
|
|
9871
10428
|
parameters: [{name: "size", type: Zcl.DataType.UINT8}],
|
|
9872
10429
|
},
|
|
@@ -9949,7 +10506,7 @@ describe("Controller", () => {
|
|
|
9949
10506
|
|
|
9950
10507
|
// coverage: prevent crash in onZclPayload when ZCL metadata validation fails
|
|
9951
10508
|
expect(mockLogger.debug).toHaveBeenCalledWith(
|
|
9952
|
-
"Ignoring attribute modelId from response: Error: modelId requires max length of 32",
|
|
10509
|
+
"Ignoring attribute modelId from response: Error: Status 'INVALID_VALUE' modelId requires max length of 32",
|
|
9953
10510
|
"zh:controller:zcl",
|
|
9954
10511
|
);
|
|
9955
10512
|
expect(device.genBasic.modelId).toStrictEqual(" other multi-endpoint device");
|
|
@@ -9959,6 +10516,7 @@ describe("Controller", () => {
|
|
|
9959
10516
|
await controller.start();
|
|
9960
10517
|
await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
|
|
9961
10518
|
const device = controller.getDeviceByIeeeAddr("0x129")!;
|
|
10519
|
+
device.addCustomCluster("hvacThermostat", CUSTOM_CLUSTERS.hvacThermostat);
|
|
9962
10520
|
const endpoint = device.getEndpoint(1)!;
|
|
9963
10521
|
const zclCommandSpy = vi.spyOn(endpoint, "zclCommand");
|
|
9964
10522
|
|
|
@@ -9985,24 +10543,24 @@ describe("Controller", () => {
|
|
|
9985
10543
|
await expect(endpoint.write("hvacThermostat", writePayloadRaw)).rejects.toThrow(
|
|
9986
10544
|
"Cannot have attributes with different manufacturerCode in single 'write' call",
|
|
9987
10545
|
);
|
|
9988
|
-
await expect(endpoint.read("hvacThermostat", readPayload)).rejects.toThrow(
|
|
10546
|
+
await expect(endpoint.read<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readPayload)).rejects.toThrow(
|
|
9989
10547
|
"Cannot have attributes with different manufacturerCode in single 'read' call",
|
|
9990
10548
|
);
|
|
9991
|
-
await expect(endpoint.read("hvacThermostat", readPayloadRaw)).rejects.toThrow(
|
|
10549
|
+
await expect(endpoint.read<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readPayloadRaw)).rejects.toThrow(
|
|
9992
10550
|
"Cannot have attributes with different manufacturerCode in single 'read' call",
|
|
9993
10551
|
);
|
|
9994
|
-
await expect(
|
|
9995
|
-
"
|
|
9996
|
-
);
|
|
9997
|
-
await expect(
|
|
9998
|
-
"
|
|
9999
|
-
);
|
|
10000
|
-
await expect(
|
|
10001
|
-
"
|
|
10002
|
-
);
|
|
10003
|
-
await expect(
|
|
10004
|
-
"
|
|
10005
|
-
);
|
|
10552
|
+
await expect(
|
|
10553
|
+
endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", configureReportingPayload),
|
|
10554
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'configureReporting' call");
|
|
10555
|
+
await expect(
|
|
10556
|
+
endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", configureReportingPayloadRaw),
|
|
10557
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'configureReporting' call");
|
|
10558
|
+
await expect(
|
|
10559
|
+
endpoint.readReportingConfig<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readReportingConfigPayload),
|
|
10560
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'readReportingConfig' call");
|
|
10561
|
+
await expect(
|
|
10562
|
+
endpoint.readReportingConfig<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readReportingConfigPayloadRaw),
|
|
10563
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'readReportingConfig' call");
|
|
10006
10564
|
|
|
10007
10565
|
//-- with override manufacturer code
|
|
10008
10566
|
|
|
@@ -10014,24 +10572,40 @@ describe("Controller", () => {
|
|
|
10014
10572
|
await expect(endpoint.write("hvacThermostat", writePayloadRaw, manufOpts)).rejects.toThrow(
|
|
10015
10573
|
"Cannot have attributes with different manufacturerCode in single 'write' call",
|
|
10016
10574
|
);
|
|
10017
|
-
await expect(
|
|
10018
|
-
"
|
|
10019
|
-
);
|
|
10020
|
-
await expect(
|
|
10021
|
-
"
|
|
10022
|
-
);
|
|
10023
|
-
await expect(
|
|
10024
|
-
"
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10575
|
+
await expect(
|
|
10576
|
+
endpoint.read<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readPayload, manufOpts),
|
|
10577
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'read' call");
|
|
10578
|
+
await expect(
|
|
10579
|
+
endpoint.read<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>("hvacThermostat", readPayloadRaw, manufOpts),
|
|
10580
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'read' call");
|
|
10581
|
+
await expect(
|
|
10582
|
+
endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>(
|
|
10583
|
+
"hvacThermostat",
|
|
10584
|
+
configureReportingPayload,
|
|
10585
|
+
manufOpts,
|
|
10586
|
+
),
|
|
10587
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'configureReporting' call");
|
|
10588
|
+
await expect(
|
|
10589
|
+
endpoint.configureReporting<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>(
|
|
10590
|
+
"hvacThermostat",
|
|
10591
|
+
configureReportingPayloadRaw,
|
|
10592
|
+
manufOpts,
|
|
10593
|
+
),
|
|
10594
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'configureReporting' call");
|
|
10595
|
+
await expect(
|
|
10596
|
+
endpoint.readReportingConfig<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>(
|
|
10597
|
+
"hvacThermostat",
|
|
10598
|
+
readReportingConfigPayload,
|
|
10599
|
+
manufOpts,
|
|
10600
|
+
),
|
|
10601
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'readReportingConfig' call");
|
|
10602
|
+
await expect(
|
|
10603
|
+
endpoint.readReportingConfig<"hvacThermostat", CustomClustersTypes["hvacThermostat"]>(
|
|
10604
|
+
"hvacThermostat",
|
|
10605
|
+
readReportingConfigPayloadRaw,
|
|
10606
|
+
manufOpts,
|
|
10607
|
+
),
|
|
10608
|
+
).rejects.toThrow("Cannot have attributes with different manufacturerCode in single 'readReportingConfig' call");
|
|
10035
10609
|
|
|
10036
10610
|
expect(zclCommandSpy).toHaveBeenCalledTimes(0);
|
|
10037
10611
|
});
|