zigbee-herdsman 5.0.4 → 6.0.1

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.
Files changed (122) hide show
  1. package/.github/dependabot.yml +3 -0
  2. package/.github/workflows/ci.yml +1 -1
  3. package/.github/workflows/typedoc.yaml +1 -1
  4. package/.release-please-manifest.json +1 -1
  5. package/CHANGELOG.md +29 -0
  6. package/biome.json +1 -1
  7. package/dist/adapter/ember/ezsp/buffalo.d.ts +0 -2
  8. package/dist/adapter/ember/ezsp/buffalo.d.ts.map +1 -1
  9. package/dist/adapter/ember/ezsp/buffalo.js +0 -4
  10. package/dist/adapter/ember/ezsp/buffalo.js.map +1 -1
  11. package/dist/adapter/ember/uart/ash.d.ts.map +1 -1
  12. package/dist/adapter/ember/uart/ash.js +0 -2
  13. package/dist/adapter/ember/uart/ash.js.map +1 -1
  14. package/dist/buffalo/buffalo.d.ts +5 -0
  15. package/dist/buffalo/buffalo.d.ts.map +1 -1
  16. package/dist/buffalo/buffalo.js +7 -0
  17. package/dist/buffalo/buffalo.js.map +1 -1
  18. package/dist/controller/controller.d.ts.map +1 -1
  19. package/dist/controller/controller.js +8 -11
  20. package/dist/controller/controller.js.map +1 -1
  21. package/dist/controller/events.d.ts +2 -1
  22. package/dist/controller/events.d.ts.map +1 -1
  23. package/dist/controller/helpers/request.d.ts.map +1 -1
  24. package/dist/controller/helpers/request.js +2 -1
  25. package/dist/controller/helpers/request.js.map +1 -1
  26. package/dist/controller/helpers/zclFrameConverter.d.ts +2 -4
  27. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  28. package/dist/controller/helpers/zclFrameConverter.js +2 -0
  29. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  30. package/dist/controller/model/device.d.ts +13 -24
  31. package/dist/controller/model/device.d.ts.map +1 -1
  32. package/dist/controller/model/device.js +88 -129
  33. package/dist/controller/model/device.js.map +1 -1
  34. package/dist/controller/model/endpoint.d.ts +17 -16
  35. package/dist/controller/model/endpoint.d.ts.map +1 -1
  36. package/dist/controller/model/endpoint.js +31 -16
  37. package/dist/controller/model/endpoint.js.map +1 -1
  38. package/dist/controller/model/group.d.ts +6 -6
  39. package/dist/controller/model/group.d.ts.map +1 -1
  40. package/dist/controller/model/group.js +5 -3
  41. package/dist/controller/model/group.js.map +1 -1
  42. package/dist/controller/model/index.d.ts +1 -0
  43. package/dist/controller/model/index.d.ts.map +1 -1
  44. package/dist/controller/model/index.js +3 -1
  45. package/dist/controller/model/index.js.map +1 -1
  46. package/dist/controller/model/zigbeeEntity.d.ts +8 -0
  47. package/dist/controller/model/zigbeeEntity.d.ts.map +1 -0
  48. package/dist/controller/model/zigbeeEntity.js +11 -0
  49. package/dist/controller/model/zigbeeEntity.js.map +1 -0
  50. package/dist/controller/tstype.d.ts +39 -0
  51. package/dist/controller/tstype.d.ts.map +1 -1
  52. package/dist/zspec/zcl/buffaloZcl.d.ts +32 -17
  53. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  54. package/dist/zspec/zcl/buffaloZcl.js +257 -121
  55. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  56. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  57. package/dist/zspec/zcl/definition/cluster.js +156 -33
  58. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  59. package/dist/zspec/zcl/definition/clusters-typegen.d.ts +2 -0
  60. package/dist/zspec/zcl/definition/clusters-typegen.d.ts.map +1 -0
  61. package/dist/zspec/zcl/definition/clusters-typegen.js +348 -0
  62. package/dist/zspec/zcl/definition/clusters-typegen.js.map +1 -0
  63. package/dist/zspec/zcl/definition/clusters-types.d.ts +7238 -0
  64. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -0
  65. package/dist/zspec/zcl/definition/clusters-types.js +3 -0
  66. package/dist/zspec/zcl/definition/clusters-types.js.map +1 -0
  67. package/dist/zspec/zcl/definition/enums.d.ts +14 -6
  68. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  69. package/dist/zspec/zcl/definition/enums.js +15 -6
  70. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  71. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  72. package/dist/zspec/zcl/definition/foundation.js +43 -15
  73. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  74. package/dist/zspec/zcl/definition/tstype.d.ts +105 -11
  75. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  76. package/dist/zspec/zcl/index.d.ts +1 -0
  77. package/dist/zspec/zcl/index.d.ts.map +1 -1
  78. package/dist/zspec/zcl/index.js.map +1 -1
  79. package/dist/zspec/zcl/utils.d.ts +1 -1
  80. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  81. package/dist/zspec/zcl/utils.js +1 -1
  82. package/dist/zspec/zcl/utils.js.map +1 -1
  83. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  84. package/dist/zspec/zcl/zclFrame.js +32 -20
  85. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  86. package/dist/zspec/zdo/buffaloZdo.d.ts +0 -6
  87. package/dist/zspec/zdo/buffaloZdo.d.ts.map +1 -1
  88. package/dist/zspec/zdo/buffaloZdo.js +0 -8
  89. package/dist/zspec/zdo/buffaloZdo.js.map +1 -1
  90. package/package.json +3 -3
  91. package/src/adapter/ember/ezsp/buffalo.ts +0 -5
  92. package/src/adapter/ember/uart/ash.ts +0 -2
  93. package/src/adapter/ezsp/driver/driver.ts +1 -1
  94. package/src/buffalo/buffalo.ts +8 -0
  95. package/src/controller/controller.ts +13 -16
  96. package/src/controller/events.ts +2 -1
  97. package/src/controller/greenPower.ts +4 -4
  98. package/src/controller/helpers/request.ts +3 -1
  99. package/src/controller/helpers/zclFrameConverter.ts +13 -17
  100. package/src/controller/model/device.ts +103 -148
  101. package/src/controller/model/endpoint.ts +112 -64
  102. package/src/controller/model/group.ts +33 -9
  103. package/src/controller/model/index.ts +1 -0
  104. package/src/controller/model/zigbeeEntity.ts +30 -0
  105. package/src/controller/tstype.ts +251 -16
  106. package/src/zspec/zcl/buffaloZcl.ts +323 -238
  107. package/src/zspec/zcl/definition/cluster.ts +156 -33
  108. package/src/zspec/zcl/definition/clusters-typegen.ts +588 -0
  109. package/src/zspec/zcl/definition/clusters-types.ts +7331 -0
  110. package/src/zspec/zcl/definition/enums.ts +14 -5
  111. package/src/zspec/zcl/definition/foundation.ts +43 -15
  112. package/src/zspec/zcl/definition/tstype.ts +118 -8
  113. package/src/zspec/zcl/index.ts +1 -0
  114. package/src/zspec/zcl/utils.ts +1 -1
  115. package/src/zspec/zcl/zclFrame.ts +37 -19
  116. package/src/zspec/zdo/buffaloZdo.ts +0 -9
  117. package/test/controller.test.ts +356 -896
  118. package/test/greenpower.test.ts +0 -12
  119. package/test/zcl.test.ts +13 -11
  120. package/test/zspec/zcl/buffalo.test.ts +216 -74
  121. package/test/zspec/zcl/frame.test.ts +62 -28
  122. package/test/zspec/zcl/utils.test.ts +4 -4
@@ -199,9 +199,12 @@ let iasZoneReadState170Count = 0;
199
199
  let enroll170 = true;
200
200
  let configureReportStatus = 0;
201
201
  let configureReportDefaultRsp = false;
202
+ let lastSentZclFrameToEndpoint: Buffer | undefined;
202
203
 
203
204
  const restoreMocksendZclFrameToEndpoint = () => {
204
205
  mocksendZclFrameToEndpoint.mockImplementation((_ieeeAddr, networkAddress, endpoint, frame: Zcl.Frame) => {
206
+ lastSentZclFrameToEndpoint = frame.toBuffer();
207
+
205
208
  if (
206
209
  frame.header.isGlobal &&
207
210
  frame.isCommand("read") &&
@@ -401,6 +404,7 @@ const mocksRestore = [mockAdapterPermitJoin, mockAdapterStop, mocksendZclFrameTo
401
404
  const events: {
402
405
  deviceJoined: Events.DeviceJoinedPayload[];
403
406
  deviceInterview: Events.DeviceInterviewPayload[];
407
+ deviceInterviewRaw: Events.DeviceInterviewPayload[];
404
408
  adapterDisconnected: number[];
405
409
  deviceAnnounce: Events.DeviceAnnouncePayload[];
406
410
  deviceLeave: Events.DeviceLeavePayload[];
@@ -411,6 +415,7 @@ const events: {
411
415
  } = {
412
416
  deviceJoined: [],
413
417
  deviceInterview: [],
418
+ deviceInterviewRaw: [],
414
419
  adapterDisconnected: [],
415
420
  deviceAnnounce: [],
416
421
  deviceLeave: [],
@@ -490,7 +495,10 @@ describe("Controller", () => {
490
495
  controller = new Controller(options);
491
496
  controller.on("permitJoinChanged", (data) => events.permitJoinChanged.push(data));
492
497
  controller.on("deviceJoined", (data) => events.deviceJoined.push(data));
493
- controller.on("deviceInterview", (data) => events.deviceInterview.push(deepClone(data)));
498
+ controller.on("deviceInterview", (data) => {
499
+ events.deviceInterview.push(deepClone(data));
500
+ events.deviceInterviewRaw.push(data);
501
+ });
494
502
  controller.on("adapterDisconnected", () => events.adapterDisconnected.push(1));
495
503
  controller.on("deviceAnnounce", (data) => events.deviceAnnounce.push(data));
496
504
  controller.on("deviceLeave", (data) => events.deviceLeave.push(data));
@@ -1286,18 +1294,21 @@ describe("Controller", () => {
1286
1294
  },
1287
1295
  ],
1288
1296
  _manufacturerID: 1212,
1289
- _manufacturerName: "KoenAndCo",
1290
- _powerSource: "Mains (single phase)",
1291
- _modelID: "myModelID",
1292
- _applicationVersion: 2,
1293
- _stackVersion: 101,
1294
- _zclVersion: 1,
1295
- _hardwareVersion: 3,
1296
- _dateCode: "201901",
1297
- _softwareBuildID: "1.01",
1298
1297
  _interviewState: InterviewState.Successful,
1299
1298
  };
1299
+ const deviceGenBasic = {
1300
+ manufacturerName: "KoenAndCo",
1301
+ powerSource: Zcl.PowerSource["Mains (single phase)"],
1302
+ modelId: "myModelID",
1303
+ appVersion: 2,
1304
+ stackVersion: 101,
1305
+ zclVersion: 1,
1306
+ hwVersion: 3,
1307
+ dateCode: "201901",
1308
+ swBuildId: "1.01",
1309
+ };
1300
1310
  expect(events.deviceInterview[1]).toStrictEqual({status: "successful", device: device});
1311
+ expect(events.deviceInterviewRaw[1].device.genBasic).toStrictEqual(deviceGenBasic);
1301
1312
  expect(deepClone(controller.getDeviceByNetworkAddress(129))).toStrictEqual(device);
1302
1313
  expect(events.deviceInterview.length).toBe(2);
1303
1314
  expect(databaseContents()).toStrictEqual(
@@ -1316,7 +1327,10 @@ describe("Controller", () => {
1316
1327
  it("Join a device and explictly accept it", async () => {
1317
1328
  controller = new Controller(options);
1318
1329
  controller.on("deviceJoined", (device) => events.deviceJoined.push(device));
1319
- controller.on("deviceInterview", (device) => events.deviceInterview.push(deepClone(device)));
1330
+ controller.on("deviceInterview", (data) => {
1331
+ events.deviceInterview.push(deepClone(data));
1332
+ events.deviceInterviewRaw.push(data);
1333
+ });
1320
1334
  await controller.start();
1321
1335
  expect(databaseContents().includes("0x129")).toBeFalsy();
1322
1336
  await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
@@ -1371,18 +1385,21 @@ describe("Controller", () => {
1371
1385
  },
1372
1386
  ],
1373
1387
  _manufacturerID: 1212,
1374
- _manufacturerName: "KoenAndCo",
1375
- _powerSource: "Mains (single phase)",
1376
- _modelID: "myModelID",
1377
- _applicationVersion: 2,
1378
- _stackVersion: 101,
1379
- _zclVersion: 1,
1380
- _hardwareVersion: 3,
1381
- _dateCode: "201901",
1382
- _softwareBuildID: "1.01",
1383
1388
  _interviewState: InterviewState.Successful,
1384
1389
  };
1390
+ const deviceGenBasic = {
1391
+ manufacturerName: "KoenAndCo",
1392
+ powerSource: Zcl.PowerSource["Mains (single phase)"],
1393
+ modelId: "myModelID",
1394
+ appVersion: 2,
1395
+ stackVersion: 101,
1396
+ zclVersion: 1,
1397
+ hwVersion: 3,
1398
+ dateCode: "201901",
1399
+ swBuildId: "1.01",
1400
+ };
1385
1401
  expect(events.deviceInterview[1]).toStrictEqual({status: "successful", device: device});
1402
+ expect(events.deviceInterviewRaw[1].device.genBasic).toStrictEqual(deviceGenBasic);
1386
1403
  expect(deepClone(controller.getDeviceByIeeeAddr("0x129"))).toStrictEqual(device);
1387
1404
  expect(events.deviceInterview.length).toBe(2);
1388
1405
  expect(databaseContents().includes("0x129")).toBeTruthy();
@@ -1429,8 +1446,8 @@ describe("Controller", () => {
1429
1446
  await controller.start();
1430
1447
  await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
1431
1448
  const device = controller.getDeviceByIeeeAddr("0x129")!;
1432
- device.powerSource = "test123";
1433
- expect(device.powerSource).toBe("test123");
1449
+ device.powerSource = "DC Source";
1450
+ expect(device.powerSource).toBe("DC Source");
1434
1451
  });
1435
1452
 
1436
1453
  it("Get device should return same instance", async () => {
@@ -2245,8 +2262,7 @@ describe("Controller", () => {
2245
2262
  expect(events.deviceInterview[1].status).toBe("successful");
2246
2263
  // @ts-expect-error private but deep cloned
2247
2264
  expect(events.deviceInterview[1].device._ieeeAddr).toBe("0x161");
2248
- // @ts-expect-error private but deep cloned
2249
- expect(events.deviceInterview[1].device._modelID).toBe("myDevice9123");
2265
+ expect(events.deviceInterviewRaw[1].device.genBasic.modelId).toBe("myDevice9123");
2250
2266
  });
2251
2267
 
2252
2268
  it("Device joins with endpoints [2,1], as 2 is the only endpoint supporting genBasic it should read modelID from that", async () => {
@@ -2259,8 +2275,7 @@ describe("Controller", () => {
2259
2275
  expect(events.deviceInterview[1].status).toBe("successful");
2260
2276
  // @ts-expect-error private but deep cloned
2261
2277
  expect(events.deviceInterview[1].device._ieeeAddr).toBe("0x162");
2262
- // @ts-expect-error private but deep cloned
2263
- expect(events.deviceInterview[1].device._modelID).toBe("myDevice9124");
2278
+ expect(events.deviceInterviewRaw[1].device.genBasic.modelId).toBe("myDevice9124");
2264
2279
  });
2265
2280
 
2266
2281
  it("Device joins and interview iAs enrollment succeeds", async () => {
@@ -2456,77 +2471,8 @@ describe("Controller", () => {
2456
2471
  const expected = {
2457
2472
  cluster: "msOccupancySensing",
2458
2473
  type: "attributeReport",
2459
- device: {
2460
- ID: 2,
2461
- _events: {},
2462
- _eventsCount: 0,
2463
- _ieeeAddr: "0x129",
2464
- _pendingRequestTimeout: 0,
2465
- _networkAddress: 129,
2466
- _lastSeen: Date.now(),
2467
- _linkquality: 50,
2468
- _skipDefaultResponse: false,
2469
- _customClusters: {},
2470
- _endpoints: [
2471
- {
2472
- ID: 1,
2473
- _events: {},
2474
- _eventsCount: 0,
2475
- inputClusters: [0, 1],
2476
- outputClusters: [2],
2477
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2478
- _binds: [],
2479
- _configuredReportings: [],
2480
- meta: {},
2481
- deviceNetworkAddress: 129,
2482
- deviceIeeeAddress: "0x129",
2483
- deviceID: 5,
2484
- profileID: 99,
2485
- clusters: {
2486
- msOccupancySensing: {
2487
- attributes: {
2488
- occupancy: 1,
2489
- },
2490
- },
2491
- },
2492
- },
2493
- ],
2494
- _type: "Router",
2495
- _manufacturerID: 1212,
2496
- _manufacturerName: "KoenAndCo",
2497
- meta: {},
2498
- _powerSource: "Mains (single phase)",
2499
- _modelID: "myModelID",
2500
- _applicationVersion: 2,
2501
- _stackVersion: 101,
2502
- _zclVersion: 1,
2503
- _hardwareVersion: 3,
2504
- _dateCode: "201901",
2505
- _softwareBuildID: "1.01",
2506
- _interviewState: InterviewState.Successful,
2507
- },
2508
- endpoint: {
2509
- ID: 1,
2510
- _events: {},
2511
- _eventsCount: 0,
2512
- deviceID: 5,
2513
- inputClusters: [0, 1],
2514
- outputClusters: [2],
2515
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2516
- deviceNetworkAddress: 129,
2517
- deviceIeeeAddress: "0x129",
2518
- _binds: [],
2519
- _configuredReportings: [],
2520
- profileID: 99,
2521
- meta: {},
2522
- clusters: {
2523
- msOccupancySensing: {
2524
- attributes: {
2525
- occupancy: 1,
2526
- },
2527
- },
2528
- },
2529
- },
2474
+ device: expect.any(Device),
2475
+ endpoint: expect.any(Endpoint),
2530
2476
  data: {
2531
2477
  occupancy: 1,
2532
2478
  },
@@ -2541,9 +2487,11 @@ describe("Controller", () => {
2541
2487
  frameType: 0,
2542
2488
  manufacturerSpecific: false,
2543
2489
  },
2490
+ manufacturerCode: undefined,
2491
+ rawData: expect.any(Buffer),
2544
2492
  },
2545
2493
  };
2546
- expect(deepClone(events.message[0])).toStrictEqual(expected);
2494
+ expect(events.message[0]).toStrictEqual(expected);
2547
2495
  expect(controller.getDeviceByIeeeAddr("0x129")!.linkquality).toEqual(50);
2548
2496
  });
2549
2497
 
@@ -2571,74 +2519,16 @@ describe("Controller", () => {
2571
2519
  const expected = {
2572
2520
  cluster: "genAlarms",
2573
2521
  type: "raw",
2574
- device: {
2575
- ID: 2,
2576
- _events: {},
2577
- _eventsCount: 0,
2578
- _pendingRequestTimeout: 0,
2579
- _ieeeAddr: "0x129",
2580
- _networkAddress: 129,
2581
- _lastSeen: Date.now(),
2582
- _linkquality: 50,
2583
- _skipDefaultResponse: false,
2584
- _customClusters: {},
2585
- _endpoints: [
2586
- {
2587
- ID: 1,
2588
- _events: {},
2589
- _eventsCount: 0,
2590
- clusters: {},
2591
- inputClusters: [0, 1],
2592
- outputClusters: [2],
2593
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2594
- deviceNetworkAddress: 129,
2595
- deviceIeeeAddress: "0x129",
2596
- _binds: [],
2597
- _configuredReportings: [],
2598
- meta: {},
2599
- deviceID: 5,
2600
- profileID: 99,
2601
- },
2602
- ],
2603
- _type: "Router",
2604
- _manufacturerID: 1212,
2605
- _manufacturerName: "KoenAndCo",
2606
- meta: {},
2607
- _powerSource: "Mains (single phase)",
2608
- _modelID: "myModelID",
2609
- _applicationVersion: 2,
2610
- _stackVersion: 101,
2611
- _zclVersion: 1,
2612
- _hardwareVersion: 3,
2613
- _dateCode: "201901",
2614
- _softwareBuildID: "1.01",
2615
- _interviewState: InterviewState.Successful,
2616
- },
2617
- endpoint: {
2618
- _events: {},
2619
- _eventsCount: 0,
2620
- clusters: {},
2621
- ID: 1,
2622
- deviceID: 5,
2623
- inputClusters: [0, 1],
2624
- outputClusters: [2],
2625
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2626
- deviceNetworkAddress: 129,
2627
- deviceIeeeAddress: "0x129",
2628
- _binds: [],
2629
- _configuredReportings: [],
2630
- profileID: 99,
2631
- meta: {},
2632
- },
2633
- data: {
2634
- data: [0, 1],
2635
- type: "Buffer",
2636
- },
2522
+ device: expect.any(Device),
2523
+ endpoint: expect.any(Endpoint),
2524
+ data: Buffer.from([0, 1]),
2637
2525
  linkquality: 50,
2638
2526
  groupID: 1,
2639
- meta: {},
2527
+ meta: {
2528
+ rawData: expect.any(Buffer),
2529
+ },
2640
2530
  };
2641
- expect(deepClone(events.message[0])).toStrictEqual(expected);
2531
+ expect(events.message[0]).toStrictEqual(expected);
2642
2532
  });
2643
2533
 
2644
2534
  it("Receive raw data from unknown cluster", async () => {
@@ -2658,74 +2548,16 @@ describe("Controller", () => {
2658
2548
  const expected = {
2659
2549
  cluster: 99999999,
2660
2550
  type: "raw",
2661
- device: {
2662
- ID: 2,
2663
- _ieeeAddr: "0x129",
2664
- _pendingRequestTimeout: 0,
2665
- _networkAddress: 129,
2666
- _lastSeen: Date.now(),
2667
- _linkquality: 50,
2668
- _skipDefaultResponse: false,
2669
- _customClusters: {},
2670
- _endpoints: [
2671
- {
2672
- ID: 1,
2673
- _events: {},
2674
- _eventsCount: 0,
2675
- clusters: {},
2676
- inputClusters: [0, 1],
2677
- outputClusters: [2],
2678
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2679
- deviceNetworkAddress: 129,
2680
- deviceIeeeAddress: "0x129",
2681
- _binds: [],
2682
- _configuredReportings: [],
2683
- meta: {},
2684
- deviceID: 5,
2685
- profileID: 99,
2686
- },
2687
- ],
2688
- _events: {},
2689
- _eventsCount: 0,
2690
- _type: "Router",
2691
- _manufacturerID: 1212,
2692
- _manufacturerName: "KoenAndCo",
2693
- meta: {},
2694
- _powerSource: "Mains (single phase)",
2695
- _modelID: "myModelID",
2696
- _applicationVersion: 2,
2697
- _stackVersion: 101,
2698
- _zclVersion: 1,
2699
- _hardwareVersion: 3,
2700
- _dateCode: "201901",
2701
- _softwareBuildID: "1.01",
2702
- _interviewState: InterviewState.Successful,
2703
- },
2704
- endpoint: {
2705
- _events: {},
2706
- _eventsCount: 0,
2707
- clusters: {},
2708
- ID: 1,
2709
- deviceID: 5,
2710
- inputClusters: [0, 1],
2711
- outputClusters: [2],
2712
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2713
- deviceNetworkAddress: 129,
2714
- deviceIeeeAddress: "0x129",
2715
- _binds: [],
2716
- _configuredReportings: [],
2717
- profileID: 99,
2718
- meta: {},
2719
- },
2720
- data: {
2721
- data: [0, 1, 2, 3],
2722
- type: "Buffer",
2723
- },
2551
+ device: expect.any(Device),
2552
+ endpoint: expect.any(Endpoint),
2553
+ data: Buffer.from([0, 1, 2, 3]),
2724
2554
  linkquality: 50,
2725
2555
  groupID: 1,
2726
- meta: {},
2556
+ meta: {
2557
+ rawData: Buffer.from([0, 1, 2, 3]),
2558
+ },
2727
2559
  };
2728
- expect(deepClone(events.message[0])).toStrictEqual(expected);
2560
+ expect(events.message[0]).toStrictEqual(expected);
2729
2561
  });
2730
2562
 
2731
2563
  it("Receive zclData from unkonwn device shouldnt emit anything", async () => {
@@ -2767,90 +2599,9 @@ describe("Controller", () => {
2767
2599
  const expected = {
2768
2600
  cluster: "genBasic",
2769
2601
  type: "readResponse",
2770
- device: {
2771
- ID: 2,
2772
- _events: {},
2773
- _eventsCount: 0,
2774
- _ieeeAddr: "0x129",
2775
- _lastSeen: Date.now(),
2776
- _pendingRequestTimeout: 0,
2777
- _linkquality: 52,
2778
- _skipDefaultResponse: false,
2779
- _networkAddress: 129,
2780
- _customClusters: {},
2781
- _endpoints: [
2782
- {
2783
- clusters: {},
2784
- ID: 1,
2785
- _events: {},
2786
- _eventsCount: 0,
2787
- inputClusters: [0, 1],
2788
- outputClusters: [2],
2789
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2790
- deviceNetworkAddress: 129,
2791
- deviceIeeeAddress: "0x129",
2792
- _binds: [],
2793
- _configuredReportings: [],
2794
- deviceID: 5,
2795
- profileID: 99,
2796
- meta: {},
2797
- },
2798
- {
2799
- ID: 3,
2800
- _events: {},
2801
- _eventsCount: 0,
2802
- clusters: {
2803
- genBasic: {
2804
- attributes: {
2805
- appVersion: 3,
2806
- },
2807
- },
2808
- },
2809
- inputClusters: [],
2810
- outputClusters: [],
2811
- pendingRequests: {id: 3, deviceIeeeAddress: "0x129", sendInProgress: false},
2812
- deviceNetworkAddress: 129,
2813
- deviceIeeeAddress: "0x129",
2814
- _binds: [],
2815
- _configuredReportings: [],
2816
- meta: {},
2817
- },
2818
- ],
2819
- _type: "Router",
2820
- _manufacturerID: 1212,
2821
- _manufacturerName: "KoenAndCo",
2822
- meta: {},
2823
- _powerSource: "Mains (single phase)",
2824
- _modelID: "myModelID",
2825
- _applicationVersion: 3,
2826
- _stackVersion: 101,
2827
- _zclVersion: 1,
2828
- _hardwareVersion: 3,
2829
- _dateCode: "201901",
2830
- _softwareBuildID: "1.01",
2831
- _interviewState: InterviewState.Successful,
2832
- _lastDefaultResponseSequenceNumber: 1,
2833
- },
2834
- endpoint: {
2835
- ID: 3,
2836
- _events: {},
2837
- _eventsCount: 0,
2838
- inputClusters: [],
2839
- outputClusters: [],
2840
- pendingRequests: {id: 3, deviceIeeeAddress: "0x129", sendInProgress: false},
2841
- meta: {},
2842
- deviceNetworkAddress: 129,
2843
- deviceIeeeAddress: "0x129",
2844
- _binds: [],
2845
- _configuredReportings: [],
2846
- clusters: {
2847
- genBasic: {
2848
- attributes: {
2849
- appVersion: 3,
2850
- },
2851
- },
2852
- },
2853
- },
2602
+ device: expect.any(Device),
2603
+ endpoint: expect.any(Endpoint),
2604
+ groupID: undefined,
2854
2605
  data: {
2855
2606
  appVersion: 3,
2856
2607
  },
@@ -2864,9 +2615,11 @@ describe("Controller", () => {
2864
2615
  frameType: 0,
2865
2616
  manufacturerSpecific: false,
2866
2617
  },
2618
+ manufacturerCode: undefined,
2619
+ rawData: expect.any(Buffer),
2867
2620
  },
2868
2621
  };
2869
- expect(deepClone(events.message[0])).toStrictEqual(expected);
2622
+ expect(events.message[0]).toStrictEqual(expected);
2870
2623
  expect(controller.getDeviceByIeeeAddr("0x129")!.endpoints.length).toBe(2);
2871
2624
  });
2872
2625
 
@@ -2890,66 +2643,8 @@ describe("Controller", () => {
2890
2643
  const expected = {
2891
2644
  cluster: "genScenes",
2892
2645
  type: "commandTradfriArrowSingle",
2893
- device: {
2894
- ID: 2,
2895
- _events: {},
2896
- _eventsCount: 0,
2897
- _pendingRequestTimeout: 0,
2898
- _lastSeen: Date.now(),
2899
- _linkquality: 19,
2900
- _skipDefaultResponse: false,
2901
- _ieeeAddr: "0x129",
2902
- _networkAddress: 129,
2903
- _customClusters: {},
2904
- _endpoints: [
2905
- {
2906
- ID: 1,
2907
- _events: {},
2908
- _eventsCount: 0,
2909
- clusters: {},
2910
- inputClusters: [0, 1],
2911
- outputClusters: [2],
2912
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2913
- deviceNetworkAddress: 129,
2914
- deviceIeeeAddress: "0x129",
2915
- _binds: [],
2916
- _configuredReportings: [],
2917
- meta: {},
2918
- deviceID: 5,
2919
- profileID: 99,
2920
- },
2921
- ],
2922
- _type: "Router",
2923
- _manufacturerID: 1212,
2924
- _manufacturerName: "KoenAndCo",
2925
- meta: {},
2926
- _powerSource: "Mains (single phase)",
2927
- _modelID: "myModelID",
2928
- _applicationVersion: 2,
2929
- _stackVersion: 101,
2930
- _zclVersion: 1,
2931
- _hardwareVersion: 3,
2932
- _dateCode: "201901",
2933
- _softwareBuildID: "1.01",
2934
- _interviewState: InterviewState.Successful,
2935
- _lastDefaultResponseSequenceNumber: 29,
2936
- },
2937
- endpoint: {
2938
- _events: {},
2939
- _eventsCount: 0,
2940
- ID: 1,
2941
- clusters: {},
2942
- inputClusters: [0, 1],
2943
- outputClusters: [2],
2944
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
2945
- deviceNetworkAddress: 129,
2946
- deviceIeeeAddress: "0x129",
2947
- _binds: [],
2948
- _configuredReportings: [],
2949
- deviceID: 5,
2950
- profileID: 99,
2951
- meta: {},
2952
- },
2646
+ device: expect.any(Device),
2647
+ endpoint: expect.any(Endpoint),
2953
2648
  data: {
2954
2649
  value: 256,
2955
2650
  value2: 13,
@@ -2966,9 +2661,10 @@ describe("Controller", () => {
2966
2661
  frameType: 1,
2967
2662
  manufacturerSpecific: true,
2968
2663
  },
2664
+ rawData: expect.any(Buffer),
2969
2665
  },
2970
2666
  };
2971
- expect(deepClone(events.message[0])).toStrictEqual(expected);
2667
+ expect(events.message[0]).toStrictEqual(expected);
2972
2668
  });
2973
2669
 
2974
2670
  it("Receive cluster command from unknown cluster", async () => {
@@ -3282,16 +2978,10 @@ describe("Controller", () => {
3282
2978
  },
3283
2979
  payload: [{attrId: 28, attrData: 3, dataType: 48, status: 0}],
3284
2980
  cluster: null,
3285
- command: {
2981
+ command: expect.objectContaining({
3286
2982
  ID: 1,
3287
2983
  name: "readRsp",
3288
- parameters: [
3289
- {name: "attrId", type: 33},
3290
- {name: "status", type: 32},
3291
- {name: "dataType", type: 32, conditions: [{type: "statusEquals", value: 0}]},
3292
- {name: "attrData", type: 1000, conditions: [{type: "statusEquals", value: 0}]},
3293
- ],
3294
- },
2984
+ }),
3295
2985
  });
3296
2986
  });
3297
2987
 
@@ -3432,12 +3122,20 @@ describe("Controller", () => {
3432
3122
  ],
3433
3123
  _type: "EndDevice",
3434
3124
  _manufacturerID: 4151,
3435
- _manufacturerName: "LUMI",
3436
3125
  meta: {},
3437
- _powerSource: "Battery",
3438
- _modelID: "lumi.occupancy",
3439
3126
  _interviewState: InterviewState.Successful,
3440
3127
  });
3128
+ expect(controller.getDeviceByIeeeAddr("0x150")?.genBasic).toStrictEqual({
3129
+ appVersion: undefined,
3130
+ dateCode: undefined,
3131
+ hwVersion: undefined,
3132
+ manufacturerName: "LUMI",
3133
+ modelId: "lumi.occupancy",
3134
+ powerSource: Zcl.PowerSource.Battery,
3135
+ stackVersion: undefined,
3136
+ swBuildId: undefined,
3137
+ zclVersion: undefined,
3138
+ });
3441
3139
  });
3442
3140
 
3443
3141
  it("Xiaomi end device joins (node descriptor succeeds, but active endpoint response fails)", async () => {
@@ -3485,12 +3183,20 @@ describe("Controller", () => {
3485
3183
  ],
3486
3184
  _type: "EndDevice",
3487
3185
  _manufacturerID: 1219,
3488
- _manufacturerName: "LUMI",
3489
3186
  meta: {},
3490
- _powerSource: "Battery",
3491
- _modelID: "lumi.occupancy",
3492
3187
  _interviewState: InterviewState.Successful,
3493
3188
  });
3189
+ expect(controller.getDeviceByIeeeAddr("0x151")?.genBasic).toStrictEqual({
3190
+ appVersion: undefined,
3191
+ dateCode: undefined,
3192
+ hwVersion: undefined,
3193
+ manufacturerName: "LUMI",
3194
+ modelId: "lumi.occupancy",
3195
+ powerSource: Zcl.PowerSource.Battery,
3196
+ stackVersion: undefined,
3197
+ swBuildId: undefined,
3198
+ zclVersion: undefined,
3199
+ });
3494
3200
  });
3495
3201
 
3496
3202
  it("Should use cached node descriptor when device is re-interviewed, but retrieve it when ignoreCache=true", async () => {
@@ -3549,97 +3255,8 @@ describe("Controller", () => {
3549
3255
  const expected = {
3550
3256
  cluster: "genBasic",
3551
3257
  type: "attributeReport",
3552
- device: {
3553
- _events: {},
3554
- _eventsCount: 0,
3555
- _lastSeen: Date.now(),
3556
- _linkquality: 50,
3557
- _skipDefaultResponse: false,
3558
- ID: 2,
3559
- _ieeeAddr: "0x129",
3560
- _networkAddress: 129,
3561
- _customClusters: {},
3562
- _endpoints: [
3563
- {
3564
- _events: {},
3565
- _eventsCount: 0,
3566
- ID: 1,
3567
- clusters: {
3568
- genBasic: {
3569
- attributes: {
3570
- "65281": {
3571
- "1": 3285,
3572
- "10": 0,
3573
- "100": 0,
3574
- "3": 33,
3575
- "4": 5032,
3576
- "5": 43,
3577
- "6": 327680,
3578
- "8": 516,
3579
- },
3580
- modelId: "lumi.sensor_wleak.aq1",
3581
- },
3582
- },
3583
- },
3584
- inputClusters: [0, 1],
3585
- outputClusters: [2],
3586
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
3587
- deviceNetworkAddress: 129,
3588
- deviceIeeeAddress: "0x129",
3589
- _binds: [],
3590
- _configuredReportings: [],
3591
- meta: {},
3592
- deviceID: 5,
3593
- profileID: 99,
3594
- },
3595
- ],
3596
- _type: "Router",
3597
- _manufacturerID: 1212,
3598
- _manufacturerName: "KoenAndCo",
3599
- meta: {},
3600
- _powerSource: "Mains (single phase)",
3601
- _modelID: "lumi.sensor_wleak.aq1",
3602
- _applicationVersion: 2,
3603
- _stackVersion: 101,
3604
- _zclVersion: 1,
3605
- _hardwareVersion: 3,
3606
- _dateCode: "201901",
3607
- _pendingRequestTimeout: 0,
3608
- _softwareBuildID: "1.01",
3609
- _interviewState: InterviewState.Successful,
3610
- },
3611
- endpoint: {
3612
- _events: {},
3613
- _eventsCount: 0,
3614
- ID: 1,
3615
- deviceID: 5,
3616
- inputClusters: [0, 1],
3617
- outputClusters: [2],
3618
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
3619
- deviceNetworkAddress: 129,
3620
- deviceIeeeAddress: "0x129",
3621
- _binds: [],
3622
- _configuredReportings: [],
3623
- profileID: 99,
3624
- meta: {},
3625
- clusters: {
3626
- genBasic: {
3627
- attributes: {
3628
- "65281": {
3629
- "1": 3285,
3630
- "10": 0,
3631
- "100": 0,
3632
- "3": 33,
3633
- "4": 5032,
3634
- "5": 43,
3635
- "6": 327680,
3636
- "8": 516,
3637
- },
3638
- modelId: "lumi.sensor_wleak.aq1",
3639
- },
3640
- },
3641
- },
3642
- },
3258
+ device: expect.any(Device),
3259
+ endpoint: expect.any(Endpoint),
3643
3260
  data: {
3644
3261
  "65281": {
3645
3262
  "1": 3285,
@@ -3665,9 +3282,10 @@ describe("Controller", () => {
3665
3282
  frameType: 0,
3666
3283
  manufacturerSpecific: true,
3667
3284
  },
3285
+ rawData: null,
3668
3286
  },
3669
3287
  };
3670
- expect(deepClone(events.message[0])).toStrictEqual(expected);
3288
+ expect(events.message[0]).toStrictEqual(expected);
3671
3289
  });
3672
3290
 
3673
3291
  it("Should allow to specify custom attributes for existing cluster", async () => {
@@ -4053,7 +3671,7 @@ describe("Controller", () => {
4053
3671
  expect(call[2]).toBe(1);
4054
3672
  expect(call[3].cluster.name).toBe("genPollCtrl");
4055
3673
  expect(call[3].command.name).toBe("checkinRsp");
4056
- expect(call[3].payload).toStrictEqual({startFastPolling: false, fastPollTimeout: 0});
3674
+ expect(call[3].payload).toStrictEqual({startFastPolling: 0, fastPollTimeout: 0});
4057
3675
  });
4058
3676
 
4059
3677
  it("Poll control unsupported", async () => {
@@ -4301,6 +3919,36 @@ describe("Controller", () => {
4301
3919
  );
4302
3920
  });
4303
3921
 
3922
+ it("throws when trying to configure reporting on endpoint with bad attribute", async () => {
3923
+ await controller.start();
3924
+ await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
3925
+ const device = controller.getDeviceByIeeeAddr("0x129")!;
3926
+ const endpoint = device.getEndpoint(1)!;
3927
+ mocksendZclFrameToEndpoint.mockClear();
3928
+
3929
+ await expect(async () => {
3930
+ await endpoint.configureReporting("genPowerCfg", [
3931
+ {
3932
+ attribute: "doesnotexist",
3933
+ minimumReportInterval: 1,
3934
+ maximumReportInterval: 10,
3935
+ reportableChange: 1,
3936
+ },
3937
+ ]);
3938
+ }).rejects.toThrow(`Invalid attribute 'doesnotexist' for cluster 'genPowerCfg'`);
3939
+
3940
+ await expect(async () => {
3941
+ await endpoint.configureReporting("genBasic", [
3942
+ {
3943
+ attribute: 99999,
3944
+ minimumReportInterval: 1,
3945
+ maximumReportInterval: 10,
3946
+ reportableChange: 1,
3947
+ },
3948
+ ]);
3949
+ }).rejects.toThrow(`Invalid attribute '99999' for cluster 'genBasic'`);
3950
+ });
3951
+
4304
3952
  it("Should replace legacy configured reportings without manufacturerCode", async () => {
4305
3953
  await controller.start();
4306
3954
  await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
@@ -4813,14 +4461,10 @@ describe("Controller", () => {
4813
4461
  },
4814
4462
  payload: {payloadType: 0, queryJitter: 1},
4815
4463
  cluster: expect.objectContaining({name: "genOta"}),
4816
- command: {
4464
+ command: expect.objectContaining({
4817
4465
  ID: 0,
4818
- parameters: [
4819
- {name: "payloadType", type: 32},
4820
- {name: "queryJitter", type: 32},
4821
- ],
4822
4466
  name: "imageNotify",
4823
- },
4467
+ }),
4824
4468
  };
4825
4469
  expect(deepClone(mocksendZclFrameToEndpoint.mock.calls[0][3])).toStrictEqual(expected);
4826
4470
  expect(mocksendZclFrameToEndpoint.mock.calls[0][4]).toBe(10000);
@@ -4868,6 +4512,24 @@ describe("Controller", () => {
4868
4512
  expect(error).toStrictEqual(new Error("whoops!"));
4869
4513
  });
4870
4514
 
4515
+ it("Endpoint waitForCommand frame fails to parse", async () => {
4516
+ await controller.start();
4517
+ await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
4518
+ const device = controller.getDeviceByIeeeAddr("0x129")!;
4519
+ const endpoint = device.getEndpoint(1)!;
4520
+ mocksendZclFrameToEndpoint.mockClear();
4521
+ // The buffer below ([24, 169, 10, 0, 0, 24]) is intentionally malformed:
4522
+ // It is missing expected payload bytes for a valid ZCL frame, causing Zcl.Frame.fromBuffer to throw a parsing error.
4523
+ // This triggers the error handling path being tested.
4524
+ const buffer = Buffer.from([24, 169, 10, 0, 0, 24]);
4525
+ const header = Zcl.Header.fromBuffer(buffer);
4526
+ const promise = new Promise((resolve, _reject) => resolve({clusterID: Zcl.Utils.getCluster("msOccupancySensing").ID, data: buffer, header}));
4527
+ mockAdapterWaitFor.mockReturnValueOnce({promise, cancel: () => {}});
4528
+ await expect(endpoint.waitForCommand("genOta", "upgradeEndRequest", 10, 20).promise).rejects.toThrow(
4529
+ `The value of "offset" is out of range. It must be >= 0 and <= 5. Received 6`,
4530
+ );
4531
+ });
4532
+
4871
4533
  it("Device without meta should set meta to {}", async () => {
4872
4534
  Device.resetCache();
4873
4535
  const line = JSON.stringify({
@@ -4907,8 +4569,6 @@ describe("Controller", () => {
4907
4569
  _eventsCount: 0,
4908
4570
  _pendingRequestTimeout: 0,
4909
4571
  _skipDefaultResponse: false,
4910
- _applicationVersion: 17,
4911
- _dateCode: "20170302",
4912
4572
  _customClusters: {},
4913
4573
  _endpoints: [
4914
4574
  {
@@ -4928,21 +4588,25 @@ describe("Controller", () => {
4928
4588
  profileID: 49246,
4929
4589
  },
4930
4590
  ],
4931
- _hardwareVersion: 1,
4932
4591
  _ieeeAddr: "0x90fd9ffffe4b64ae",
4933
4592
  _interviewState: InterviewState.Successful,
4934
4593
  _manufacturerID: 4476,
4935
- _manufacturerName: "IKEA of Sweden",
4936
4594
  meta: {},
4937
- _modelID: "TRADFRI remote control",
4938
4595
  _networkAddress: 19468,
4939
- _powerSource: "Battery",
4940
- _softwareBuildID: "1.2.214",
4941
- _stackVersion: 87,
4942
4596
  _type: "EndDevice",
4943
- _zclVersion: 1,
4944
4597
  };
4945
4598
  expect(deepClone(controller.getDeviceByIeeeAddr("0x90fd9ffffe4b64ae"))).toStrictEqual(expected);
4599
+ expect(controller.getDeviceByIeeeAddr("0x90fd9ffffe4b64ae")?.genBasic).toStrictEqual({
4600
+ manufacturerName: "IKEA of Sweden",
4601
+ modelId: "TRADFRI remote control",
4602
+ powerSource: Zcl.PowerSource.Battery,
4603
+ swBuildId: "1.2.214",
4604
+ stackVersion: 87,
4605
+ zclVersion: 1,
4606
+ appVersion: 17,
4607
+ dateCode: "20170302",
4608
+ hwVersion: 1,
4609
+ });
4946
4610
  });
4947
4611
 
4948
4612
  it("Read from group", async () => {
@@ -4990,7 +4654,7 @@ describe("Controller", () => {
4990
4654
  it("Write to group", async () => {
4991
4655
  await controller.start();
4992
4656
  const group = await controller.createGroup(2);
4993
- await group.write("genBasic", {49: {value: 0x000b, type: 0x19}, deviceEnabled: true}, {});
4657
+ await group.write("genBasic", {49: {value: 0x000b, type: 0x19}, deviceEnabled: 1}, {});
4994
4658
  expect(mocksendZclFrameToGroup).toHaveBeenCalledTimes(1);
4995
4659
  expect(mocksendZclFrameToGroup.mock.calls[0][0]).toBe(2);
4996
4660
  expect(deepClone(mocksendZclFrameToGroup.mock.calls[0][1])).toStrictEqual(
@@ -5005,7 +4669,7 @@ describe("Controller", () => {
5005
4669
  0,
5006
4670
  [
5007
4671
  {attrData: 11, attrId: 49, dataType: 25},
5008
- {attrData: true, attrId: 18, dataType: 16},
4672
+ {attrData: 1, attrId: 18, dataType: 16},
5009
4673
  ],
5010
4674
  {},
5011
4675
  ),
@@ -5019,7 +4683,7 @@ describe("Controller", () => {
5019
4683
  const group = await controller.createGroup(2);
5020
4684
  let error;
5021
4685
  try {
5022
- await group.write("genBasic", {UNKNOWN: {value: 0x000b, type: 0x19}, deviceEnabled: true}, {});
4686
+ await group.write("genBasic", {UNKNOWN: {value: 0x000b, type: 0x19}, deviceEnabled: 1}, {});
5023
4687
  } catch (e) {
5024
4688
  error = e;
5025
4689
  }
@@ -5710,8 +5374,6 @@ describe("Controller", () => {
5710
5374
  _eventsCount: 0,
5711
5375
  _pendingRequestTimeout: 0,
5712
5376
  _skipDefaultResponse: false,
5713
- _applicationVersion: 17,
5714
- _dateCode: "20170331",
5715
5377
  _customClusters: {},
5716
5378
  _endpoints: [
5717
5379
  {
@@ -5731,27 +5393,29 @@ describe("Controller", () => {
5731
5393
  profileID: 49246,
5732
5394
  },
5733
5395
  ],
5734
- _hardwareVersion: 1,
5735
5396
  _ieeeAddr: "0x000b57fffec6a5b2",
5736
5397
  _interviewState: InterviewState.Successful,
5737
5398
  _manufacturerID: 4476,
5738
- _manufacturerName: "IKEA of Sweden",
5739
5399
  meta: {reporting: 1},
5740
- _modelID: "TRADFRI bulb E27 WS opal 980lm",
5741
5400
  _networkAddress: 40369,
5742
- _powerSource: "Mains (single phase)",
5743
- _softwareBuildID: "1.2.217",
5744
- _stackVersion: 87,
5745
5401
  _type: "Router",
5746
- _zclVersion: 1,
5402
+ });
5403
+ expect(controller.getDeviceByIeeeAddr("0x000b57fffec6a5b2")?.genBasic).toStrictEqual({
5404
+ appVersion: 17,
5405
+ dateCode: "20170331",
5406
+ hwVersion: 1,
5407
+ manufacturerName: "IKEA of Sweden",
5408
+ modelId: "TRADFRI bulb E27 WS opal 980lm",
5409
+ powerSource: Zcl.PowerSource["Mains (single phase)"],
5410
+ swBuildId: "1.2.217",
5411
+ stackVersion: 87,
5412
+ zclVersion: 1,
5747
5413
  });
5748
5414
  expect(deepClone(controller.getDeviceByIeeeAddr("0x0017880104e45517"))).toStrictEqual({
5749
5415
  ID: 4,
5750
5416
  _events: {},
5751
5417
  _eventsCount: 0,
5752
5418
  _pendingRequestTimeout: 0,
5753
- _applicationVersion: 2,
5754
- _dateCode: "20160302",
5755
5419
  _customClusters: {},
5756
5420
  _endpoints: [
5757
5421
  {
@@ -5787,30 +5451,32 @@ describe("Controller", () => {
5787
5451
  pendingRequests: {id: 2, deviceIeeeAddress: "0x0017880104e45517", sendInProgress: false},
5788
5452
  },
5789
5453
  ],
5790
- _hardwareVersion: 1,
5791
5454
  _ieeeAddr: "0x0017880104e45517",
5792
5455
  _interviewState: InterviewState.Successful,
5793
5456
  _lastSeen: 123,
5794
5457
  _manufacturerID: 4107,
5795
- _manufacturerName: "Philips",
5796
- _modelID: "RWL021",
5797
5458
  _networkAddress: 6538,
5798
- _powerSource: "Battery",
5799
- _softwareBuildID: "5.45.1.17846",
5800
- _stackVersion: 1,
5801
5459
  _type: "EndDevice",
5802
- _zclVersion: 1,
5803
5460
  _skipDefaultResponse: false,
5804
5461
  meta: {configured: 1},
5805
5462
  });
5463
+ expect(controller.getDeviceByIeeeAddr("0x0017880104e45517")?.genBasic).toStrictEqual({
5464
+ appVersion: 2,
5465
+ dateCode: "20160302",
5466
+ hwVersion: 1,
5467
+ manufacturerName: "Philips",
5468
+ modelId: "RWL021",
5469
+ powerSource: Zcl.PowerSource.Battery,
5470
+ swBuildId: "5.45.1.17846",
5471
+ stackVersion: 1,
5472
+ zclVersion: 1,
5473
+ });
5806
5474
  expect(deepClone(controller.getDeviceByIeeeAddr("0x0017880104e45518"))).toStrictEqual({
5807
5475
  ID: 6,
5808
5476
  _checkinInterval: 123456,
5809
5477
  _events: {},
5810
5478
  _eventsCount: 0,
5811
5479
  _pendingRequestTimeout: 123456000,
5812
- _applicationVersion: 2,
5813
- _dateCode: "20160302",
5814
5480
  _customClusters: {},
5815
5481
  _endpoints: [
5816
5482
  {
@@ -5846,21 +5512,25 @@ describe("Controller", () => {
5846
5512
  pendingRequests: {id: 2, deviceIeeeAddress: "0x0017880104e45518", sendInProgress: false},
5847
5513
  },
5848
5514
  ],
5849
- _hardwareVersion: 1,
5850
5515
  _ieeeAddr: "0x0017880104e45518",
5851
5516
  _interviewState: InterviewState.Successful,
5852
5517
  _manufacturerID: 4107,
5853
- _manufacturerName: "Philips",
5854
- _modelID: "RWL021",
5855
5518
  _networkAddress: 6536,
5856
- _powerSource: "Battery",
5857
- _softwareBuildID: "5.45.1.17846",
5858
- _stackVersion: 1,
5859
5519
  _type: "EndDevice",
5860
- _zclVersion: 1,
5861
5520
  _skipDefaultResponse: false,
5862
5521
  meta: {configured: 1},
5863
5522
  });
5523
+ expect(controller.getDeviceByIeeeAddr("0x0017880104e45518")?.genBasic).toStrictEqual({
5524
+ appVersion: 2,
5525
+ dateCode: "20160302",
5526
+ hwVersion: 1,
5527
+ manufacturerName: "Philips",
5528
+ modelId: "RWL021",
5529
+ powerSource: Zcl.PowerSource.Battery,
5530
+ swBuildId: "5.45.1.17846",
5531
+ stackVersion: 1,
5532
+ zclVersion: 1,
5533
+ });
5864
5534
  expect((await controller.getGroups()).length).toBe(2);
5865
5535
 
5866
5536
  const group1 = controller.getGroupByID(1)!;
@@ -6002,65 +5672,8 @@ describe("Controller", () => {
6002
5672
 
6003
5673
  const expected = {
6004
5674
  type: "read",
6005
- device: {
6006
- ID: 2,
6007
- _applicationVersion: 2,
6008
- _dateCode: "201901",
6009
- _pendingRequestTimeout: 0,
6010
- _customClusters: {},
6011
- _endpoints: [
6012
- {
6013
- deviceID: 5,
6014
- inputClusters: [0, 1],
6015
- outputClusters: [2],
6016
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
6017
- profileID: 99,
6018
- ID: 1,
6019
- clusters: {},
6020
- deviceIeeeAddress: "0x129",
6021
- deviceNetworkAddress: 129,
6022
- _binds: [],
6023
- _configuredReportings: [],
6024
- _events: {},
6025
- _eventsCount: 0,
6026
- meta: {},
6027
- },
6028
- ],
6029
- _hardwareVersion: 3,
6030
- _events: {},
6031
- _eventsCount: 0,
6032
- _ieeeAddr: "0x129",
6033
- _interviewState: InterviewState.Successful,
6034
- _lastSeen: Date.now(),
6035
- _linkquality: 19,
6036
- _skipDefaultResponse: false,
6037
- _manufacturerID: 1212,
6038
- _manufacturerName: "KoenAndCo",
6039
- _modelID: "myModelID",
6040
- _networkAddress: 129,
6041
- _powerSource: "Mains (single phase)",
6042
- _softwareBuildID: "1.01",
6043
- _stackVersion: 101,
6044
- _type: "Router",
6045
- _zclVersion: 1,
6046
- meta: {},
6047
- },
6048
- endpoint: {
6049
- deviceID: 5,
6050
- inputClusters: [0, 1],
6051
- outputClusters: [2],
6052
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
6053
- profileID: 99,
6054
- ID: 1,
6055
- clusters: {},
6056
- deviceIeeeAddress: "0x129",
6057
- deviceNetworkAddress: 129,
6058
- _binds: [],
6059
- _configuredReportings: [],
6060
- _events: {},
6061
- _eventsCount: 0,
6062
- meta: {},
6063
- },
5675
+ device: expect.any(Device),
5676
+ endpoint: expect.any(Endpoint),
6064
5677
  data: ["mainsVoltage", 9999],
6065
5678
  linkquality: 19,
6066
5679
  groupID: 10,
@@ -6074,11 +5687,13 @@ describe("Controller", () => {
6074
5687
  frameType: 0,
6075
5688
  manufacturerSpecific: false,
6076
5689
  },
5690
+ manufacturerCode: undefined,
5691
+ rawData: expect.any(Buffer),
6077
5692
  },
6078
5693
  };
6079
5694
 
6080
5695
  expect(events.message.length).toBe(1);
6081
- expect(deepClone(events.message[0])).toStrictEqual(expected);
5696
+ expect(events.message[0]).toStrictEqual(expected);
6082
5697
  });
6083
5698
 
6084
5699
  it("Emit write from device", async () => {
@@ -6099,65 +5714,8 @@ describe("Controller", () => {
6099
5714
 
6100
5715
  const expected = {
6101
5716
  type: "write",
6102
- device: {
6103
- ID: 2,
6104
- _events: {},
6105
- _eventsCount: 0,
6106
- _applicationVersion: 2,
6107
- _dateCode: "201901",
6108
- _pendingRequestTimeout: 0,
6109
- _customClusters: {},
6110
- _endpoints: [
6111
- {
6112
- meta: {},
6113
- deviceID: 5,
6114
- _events: {},
6115
- _eventsCount: 0,
6116
- inputClusters: [0, 1],
6117
- outputClusters: [2],
6118
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
6119
- profileID: 99,
6120
- ID: 1,
6121
- clusters: {},
6122
- deviceIeeeAddress: "0x129",
6123
- deviceNetworkAddress: 129,
6124
- _binds: [],
6125
- _configuredReportings: [],
6126
- },
6127
- ],
6128
- _hardwareVersion: 3,
6129
- _ieeeAddr: "0x129",
6130
- _interviewState: InterviewState.Successful,
6131
- _lastSeen: Date.now(),
6132
- _linkquality: 19,
6133
- _skipDefaultResponse: false,
6134
- _manufacturerID: 1212,
6135
- _manufacturerName: "KoenAndCo",
6136
- _modelID: "myModelID",
6137
- _networkAddress: 129,
6138
- _powerSource: "Mains (single phase)",
6139
- _softwareBuildID: "1.01",
6140
- _stackVersion: 101,
6141
- _type: "Router",
6142
- _zclVersion: 1,
6143
- meta: {},
6144
- },
6145
- endpoint: {
6146
- _events: {},
6147
- _eventsCount: 0,
6148
- deviceID: 5,
6149
- inputClusters: [0, 1],
6150
- outputClusters: [2],
6151
- pendingRequests: {id: 1, deviceIeeeAddress: "0x129", sendInProgress: false},
6152
- profileID: 99,
6153
- ID: 1,
6154
- clusters: {},
6155
- deviceIeeeAddress: "0x129",
6156
- deviceNetworkAddress: 129,
6157
- _binds: [],
6158
- _configuredReportings: [],
6159
- meta: {},
6160
- },
5717
+ device: expect.any(Device),
5718
+ endpoint: expect.any(Endpoint),
6161
5719
  data: {
6162
5720
  "16389": 3,
6163
5721
  },
@@ -6173,11 +5731,13 @@ describe("Controller", () => {
6173
5731
  frameType: 0,
6174
5732
  manufacturerSpecific: false,
6175
5733
  },
5734
+ manufacturerCode: undefined,
5735
+ rawData: expect.any(Buffer),
6176
5736
  },
6177
5737
  };
6178
5738
 
6179
5739
  expect(events.message.length).toBe(1);
6180
- expect(deepClone(events.message[0])).toStrictEqual(expected);
5740
+ expect(events.message[0]).toStrictEqual(expected);
6181
5741
  });
6182
5742
 
6183
5743
  it("Endpoint command error", async () => {
@@ -6401,13 +5961,13 @@ describe("Controller", () => {
6401
5961
  mocksendZclFrameToEndpoint.mockRejectedValueOnce(new Error("timeout occurred"));
6402
5962
  let error;
6403
5963
  try {
6404
- await endpoint.readResponse("genOnOff", 1, [{onOff: 1}]);
5964
+ await endpoint.readResponse("genOnOff", 1, {onOff: 1});
6405
5965
  } catch (e) {
6406
5966
  error = e;
6407
5967
  }
6408
5968
  expect(error).toStrictEqual(
6409
5969
  new Error(
6410
- `ZCL command 0x129/1 genOnOff.readRsp([{"onOff":1}], {"timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":1,"reservedBits":0,"transactionSequenceNumber":1,"writeUndiv":false}) failed (timeout occurred)`,
5970
+ `ZCL command 0x129/1 genOnOff.readRsp({"onOff":1}, {"timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":1,"reservedBits":0,"transactionSequenceNumber":1,"writeUndiv":false}) failed (timeout occurred)`,
6411
5971
  ),
6412
5972
  );
6413
5973
  });
@@ -6484,25 +6044,23 @@ describe("Controller", () => {
6484
6044
  });
6485
6045
 
6486
6046
  it("Write structured", async () => {
6487
- await controller.start();
6488
6047
  await controller.start();
6489
6048
  await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
6490
6049
  const device = controller.getDeviceByIeeeAddr("0x129")!;
6491
6050
  const endpoint = device.getEndpoint(1)!;
6492
6051
  mocksendZclFrameToEndpoint.mockReturnValueOnce(null);
6493
6052
 
6494
- await endpoint.writeStructured("genPowerCfg", {});
6053
+ await endpoint.writeStructured("genPowerCfg", []);
6495
6054
  });
6496
6055
 
6497
6056
  it("Write structured with disable response", async () => {
6498
- await controller.start();
6499
6057
  await controller.start();
6500
6058
  await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
6501
6059
  const device = controller.getDeviceByIeeeAddr("0x129")!;
6502
6060
  const endpoint = device.getEndpoint(1)!;
6503
6061
  mocksendZclFrameToEndpoint.mockReturnValueOnce(null);
6504
6062
 
6505
- await endpoint.writeStructured("genPowerCfg", {}, {disableResponse: true});
6063
+ await endpoint.writeStructured("genPowerCfg", [], {disableResponse: true});
6506
6064
  });
6507
6065
 
6508
6066
  it("Write structured error", async () => {
@@ -6513,17 +6071,71 @@ describe("Controller", () => {
6513
6071
  mocksendZclFrameToEndpoint.mockRejectedValueOnce(new Error("timeout occurred"));
6514
6072
  let error;
6515
6073
  try {
6516
- await endpoint.writeStructured("genPowerCfg", {});
6074
+ await endpoint.writeStructured("genPowerCfg", []);
6517
6075
  } catch (e) {
6518
6076
  error = e;
6519
6077
  }
6520
6078
  expect(error).toStrictEqual(
6521
6079
  new Error(
6522
- `ZCL command 0x129/1 genPowerCfg.writeStructured({}, {"timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"reservedBits":0,"writeUndiv":false}) failed (timeout occurred)`,
6080
+ `ZCL command 0x129/1 genPowerCfg.writeStructured([], {"timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"reservedBits":0,"writeUndiv":false}) failed (timeout occurred)`,
6523
6081
  ),
6524
6082
  );
6525
6083
  });
6526
6084
 
6085
+ it("Write with custom payload", async () => {
6086
+ await controller.start();
6087
+ await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
6088
+ const device = controller.getDeviceByIeeeAddr("0x129")!;
6089
+ const endpoint = device.getEndpoint(1)!;
6090
+
6091
+ const writeOptions = {
6092
+ frameType: 0,
6093
+ manufacturerCode: 0x1ad2,
6094
+ disableDefaultResponse: true,
6095
+ disableResponse: true,
6096
+ reservedBits: 3,
6097
+ direction: 1,
6098
+ writeUndiv: true,
6099
+ transactionSequenceNumber: 0xe9,
6100
+ };
6101
+
6102
+ await endpoint.writeStructured(
6103
+ "genPowerCfg",
6104
+ [
6105
+ {
6106
+ attrId: 0x0000,
6107
+ // @ts-expect-error workaround write custom payload, special case "do not write anything"
6108
+ selector: null,
6109
+ elementData: [0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
6110
+ // dataType: undefined,
6111
+ },
6112
+ ],
6113
+ writeOptions,
6114
+ );
6115
+
6116
+ expect(lastSentZclFrameToEndpoint).toStrictEqual(
6117
+ // Note: 0x00 before start of payload is from having dataType=undefined (gets written as zero)
6118
+ Buffer.from([0x7c, 0xd2, 0x1a, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
6119
+ );
6120
+
6121
+ await endpoint.write(
6122
+ "genPowerCfg",
6123
+ {
6124
+ // @ts-expect-error workaround write custom payload
6125
+ 4865: {
6126
+ value: [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
6127
+ // type: undefined,
6128
+ },
6129
+ },
6130
+ writeOptions,
6131
+ );
6132
+
6133
+ expect(lastSentZclFrameToEndpoint).toStrictEqual(
6134
+ // Note: 0x00 before start of payload is from having dataType=undefined (gets written as zero)
6135
+ Buffer.from([0x7c, 0xd2, 0x1a, 0xe9, 0x03, 0x01, 0x13, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
6136
+ );
6137
+ });
6138
+
6527
6139
  it("Green power", async () => {
6528
6140
  await controller.start();
6529
6141
  const data = {
@@ -6624,7 +6236,6 @@ describe("Controller", () => {
6624
6236
  _interviewState: InterviewState.Successful,
6625
6237
  _lastSeen: Date.now(),
6626
6238
  _linkquality: 50,
6627
- _modelID: "GreenPower_2",
6628
6239
  _networkAddress: 0xf4fe,
6629
6240
  _type: "GreenPower",
6630
6241
  meta: {},
@@ -6644,13 +6255,16 @@ describe("Controller", () => {
6644
6255
  _eventsCount: 0,
6645
6256
  _ieeeAddr: "0x000000000046f4fe",
6646
6257
  _interviewState: InterviewState.Successful,
6647
- _modelID: "GreenPower_2",
6648
6258
  _networkAddress: 0xf4fe,
6649
6259
  _type: "GreenPower",
6650
6260
  meta: {},
6651
6261
  _gpSecurityKey: [0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab],
6652
6262
  },
6653
6263
  });
6264
+ expect(deepClone(events.deviceInterviewRaw[0].device.genBasic)).toStrictEqual({
6265
+ modelId: "GreenPower_2",
6266
+ powerSource: 0,
6267
+ });
6654
6268
  expect(controller.getDeviceByIeeeAddr("0x000000000046f4fe")!.networkAddress).toBe(0xf4fe);
6655
6269
  expect(events.message.length).toBe(2);
6656
6270
 
@@ -6681,53 +6295,8 @@ describe("Controller", () => {
6681
6295
  expect(events.message.length).toBe(1);
6682
6296
  const expected = {
6683
6297
  type: "commandNotification",
6684
- device: {
6685
- ID: 2,
6686
- _events: {},
6687
- _eventsCount: 0,
6688
- _pendingRequestTimeout: 0,
6689
- _customClusters: {},
6690
- _endpoints: [
6691
- {
6692
- inputClusters: [],
6693
- meta: {},
6694
- outputClusters: [],
6695
- pendingRequests: {id: ZSpec.GP_ENDPOINT, deviceIeeeAddress: "0x000000000046f4fe", sendInProgress: false},
6696
- ID: ZSpec.GP_ENDPOINT,
6697
- _events: {},
6698
- _eventsCount: 0,
6699
- clusters: {},
6700
- deviceIeeeAddress: "0x000000000046f4fe",
6701
- deviceNetworkAddress: 0xf4fe,
6702
- _binds: [],
6703
- _configuredReportings: [],
6704
- },
6705
- ],
6706
- _ieeeAddr: "0x000000000046f4fe",
6707
- _interviewState: InterviewState.Successful,
6708
- _lastSeen: Date.now(),
6709
- _linkquality: 50,
6710
- _skipDefaultResponse: false,
6711
- _modelID: "GreenPower_2",
6712
- _networkAddress: 0xf4fe,
6713
- _type: "GreenPower",
6714
- meta: {},
6715
- _gpSecurityKey: [0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab],
6716
- },
6717
- endpoint: {
6718
- inputClusters: [],
6719
- meta: {},
6720
- outputClusters: [],
6721
- pendingRequests: {id: ZSpec.GP_ENDPOINT, deviceIeeeAddress: "0x000000000046f4fe", sendInProgress: false},
6722
- ID: ZSpec.GP_ENDPOINT,
6723
- _events: {},
6724
- _eventsCount: 0,
6725
- clusters: {},
6726
- deviceIeeeAddress: "0x000000000046f4fe",
6727
- deviceNetworkAddress: 0xf4fe,
6728
- _binds: [],
6729
- _configuredReportings: [],
6730
- },
6298
+ device: expect.any(Device),
6299
+ endpoint: expect.any(Endpoint),
6731
6300
  data: {options: 0, srcID: 0x46f4fe, frameCounter: 228, commandID: 34, payloadSize: 255, commandFrame: {}},
6732
6301
  linkquality: 50,
6733
6302
  groupID: 1,
@@ -6735,9 +6304,11 @@ describe("Controller", () => {
6735
6304
  meta: {
6736
6305
  zclTransactionSequenceNumber: 10,
6737
6306
  frameControl: {reservedBits: 0, frameType: 1, direction: 0, disableDefaultResponse: true, manufacturerSpecific: false},
6307
+ manufacturerCode: undefined,
6308
+ rawData: expect.any(Buffer),
6738
6309
  },
6739
6310
  };
6740
- expect(deepClone(events.message[0])).toStrictEqual(expected);
6311
+ expect(events.message[0]).toStrictEqual(expected);
6741
6312
 
6742
6313
  const identifyUnknownDeviceSpy = vi.spyOn(controller, "identifyUnknownDevice");
6743
6314
 
@@ -7262,13 +6833,13 @@ describe("Controller", () => {
7262
6833
  _interviewState: InterviewState.Successful,
7263
6834
  _lastSeen: Date.now(),
7264
6835
  _linkquality: 50,
7265
- _modelID: "GreenPower_2",
7266
6836
  _networkAddress: 0x71f8,
7267
6837
  _type: "GreenPower",
7268
6838
  meta: {},
7269
6839
  _gpSecurityKey: [0x21, 0x7f, 0x8c, 0xb2, 0x90, 0xd9, 0x90, 0x14, 0x15, 0xd0, 0x5c, 0xb1, 0x64, 0x7c, 0x44, 0x6c],
7270
6840
  },
7271
6841
  });
6842
+ expect(controller.getDeviceByIeeeAddr("0x00000000017171f8")?.genBasic.modelId).toStrictEqual("GreenPower_2");
7272
6843
  expect(events.deviceInterview.length).toBe(3); // gpp[started] + gpp[successful] + gpd
7273
6844
  expect(deepClone(events.deviceInterview[2])).toStrictEqual({
7274
6845
  status: "successful",
@@ -7282,13 +6853,16 @@ describe("Controller", () => {
7282
6853
  _endpoints: [],
7283
6854
  _ieeeAddr: "0x00000000017171f8",
7284
6855
  _interviewState: InterviewState.Successful,
7285
- _modelID: "GreenPower_2",
7286
6856
  _networkAddress: 0x71f8,
7287
6857
  _type: "GreenPower",
7288
6858
  meta: {},
7289
6859
  _gpSecurityKey: [0x21, 0x7f, 0x8c, 0xb2, 0x90, 0xd9, 0x90, 0x14, 0x15, 0xd0, 0x5c, 0xb1, 0x64, 0x7c, 0x44, 0x6c],
7290
6860
  },
7291
6861
  });
6862
+ expect(deepClone(events.deviceInterviewRaw[2].device.genBasic)).toStrictEqual({
6863
+ modelId: "GreenPower_2",
6864
+ powerSource: Zcl.PowerSource.Unknown,
6865
+ });
7292
6866
  expect(controller.getDeviceByIeeeAddr("0x00000000017171f8")!.networkAddress).toBe(0x71f8);
7293
6867
  expect(events.message.length).toBe(2);
7294
6868
 
@@ -7320,53 +6894,8 @@ describe("Controller", () => {
7320
6894
  expect(events.message.length).toBe(1);
7321
6895
  const expected = {
7322
6896
  type: "commandNotification",
7323
- device: {
7324
- _events: {},
7325
- _eventsCount: 0,
7326
- ID: 3,
7327
- _type: "GreenPower",
7328
- _ieeeAddr: "0x00000000017171f8",
7329
- _networkAddress: 29176,
7330
- _customClusters: {},
7331
- _endpoints: [
7332
- {
7333
- _events: {},
7334
- _eventsCount: 0,
7335
- ID: ZSpec.GP_ENDPOINT,
7336
- inputClusters: [],
7337
- outputClusters: [],
7338
- deviceNetworkAddress: 29176,
7339
- deviceIeeeAddress: "0x00000000017171f8",
7340
- clusters: {},
7341
- _binds: [],
7342
- _configuredReportings: [],
7343
- meta: {},
7344
- pendingRequests: {sendInProgress: false, id: ZSpec.GP_ENDPOINT, deviceIeeeAddress: "0x00000000017171f8"},
7345
- },
7346
- ],
7347
- _modelID: "GreenPower_2",
7348
- _interviewState: InterviewState.Successful,
7349
- _skipDefaultResponse: false,
7350
- meta: {},
7351
- _gpSecurityKey: [0x21, 0x7f, 0x8c, 0xb2, 0x90, 0xd9, 0x90, 0x14, 0x15, 0xd0, 0x5c, 0xb1, 0x64, 0x7c, 0x44, 0x6c],
7352
- _lastSeen: Date.now(),
7353
- _pendingRequestTimeout: 0,
7354
- _linkquality: 50,
7355
- },
7356
- endpoint: {
7357
- _events: {},
7358
- _eventsCount: 0,
7359
- ID: ZSpec.GP_ENDPOINT,
7360
- inputClusters: [],
7361
- outputClusters: [],
7362
- deviceNetworkAddress: 29176,
7363
- deviceIeeeAddress: "0x00000000017171f8",
7364
- clusters: {},
7365
- _binds: [],
7366
- _configuredReportings: [],
7367
- meta: {},
7368
- pendingRequests: {sendInProgress: false, id: ZSpec.GP_ENDPOINT, deviceIeeeAddress: "0x00000000017171f8"},
7369
- },
6897
+ device: expect.any(Device),
6898
+ endpoint: expect.any(Endpoint),
7370
6899
  data: {
7371
6900
  options: 21640,
7372
6901
  srcID: 24211960,
@@ -7383,9 +6912,11 @@ describe("Controller", () => {
7383
6912
  meta: {
7384
6913
  zclTransactionSequenceNumber: 10,
7385
6914
  frameControl: {reservedBits: 0, frameType: 1, direction: 0, disableDefaultResponse: true, manufacturerSpecific: false},
6915
+ manufacturerCode: undefined,
6916
+ rawData: expect.any(Buffer),
7386
6917
  },
7387
6918
  };
7388
- expect(deepClone(events.message[0])).toStrictEqual(expected);
6919
+ expect(events.message[0]).toStrictEqual(expected);
7389
6920
 
7390
6921
  // Remove green power device from network
7391
6922
  const removeCommand = {
@@ -7431,12 +6962,12 @@ describe("Controller", () => {
7431
6962
  _interviewState: InterviewState.Successful,
7432
6963
  _lastSeen: Date.now(),
7433
6964
  _linkquality: 50,
7434
- _modelID: "GreenPower_2",
7435
6965
  _networkAddress: 0x71f8,
7436
6966
  _type: "GreenPower",
7437
6967
  meta: {},
7438
6968
  _gpSecurityKey: [0x21, 0x7f, 0x8c, 0xb2, 0x90, 0xd9, 0x90, 0x14, 0x15, 0xd0, 0x5c, 0xb1, 0x64, 0x7c, 0x44, 0x6c],
7439
6969
  });
6970
+ expect(Device.byIeeeAddr("0x00000000017171f8", true)?.genBasic.modelId).toStrictEqual("GreenPower_2");
7440
6971
 
7441
6972
  // Re-add device
7442
6973
  vi.spyOn(Zcl.Frame, "fromBuffer").mockReturnValueOnce(expectedFrame); // Mock because no Buffalo write for 0xe0 is implemented
@@ -7478,12 +7009,12 @@ describe("Controller", () => {
7478
7009
  _interviewState: InterviewState.Successful,
7479
7010
  _lastSeen: Date.now(),
7480
7011
  _linkquality: 50,
7481
- _modelID: "GreenPower_2",
7482
7012
  _networkAddress: 0x71f8,
7483
7013
  _type: "GreenPower",
7484
7014
  meta: {},
7485
7015
  _gpSecurityKey: [0x21, 0x7f, 0x8c, 0xb2, 0x90, 0xd9, 0x90, 0x14, 0x15, 0xd0, 0x5c, 0xb1, 0x64, 0x7c, 0x44, 0x6c],
7486
7016
  });
7017
+ expect(Device.byIeeeAddr("0x00000000017171f8")?.genBasic.modelId).toStrictEqual("GreenPower_2");
7487
7018
  });
7488
7019
 
7489
7020
  it("Get input/ouptut clusters", async () => {
@@ -8068,7 +7599,7 @@ describe("Controller", () => {
8068
7599
  expect(checkinrsp[2]).toBe(1);
8069
7600
  expect(checkinrsp[3].cluster.name).toBe("genPollCtrl");
8070
7601
  expect(checkinrsp[3].command.name).toBe("checkinRsp");
8071
- expect(checkinrsp[3].payload).toStrictEqual({startFastPolling: true, fastPollTimeout: 0});
7602
+ expect(checkinrsp[3].payload).toStrictEqual({startFastPolling: 1, fastPollTimeout: 0});
8072
7603
 
8073
7604
  expect(await result).toBe(undefined);
8074
7605
 
@@ -8109,135 +7640,8 @@ describe("Controller", () => {
8109
7640
 
8110
7641
  const expected = {
8111
7642
  type: "read",
8112
- device: {
8113
- ID: 3,
8114
- _applicationVersion: 2,
8115
- _dateCode: "201901",
8116
- _pendingRequestTimeout: 0,
8117
- _customClusters: {},
8118
- _endpoints: [
8119
- {
8120
- deviceID: 5,
8121
- inputClusters: [0, 1, 2],
8122
- outputClusters: [2],
8123
- profileID: 99,
8124
- ID: 1,
8125
- clusters: {},
8126
- deviceIeeeAddress: "0x171",
8127
- deviceNetworkAddress: 171,
8128
- _binds: [],
8129
- _configuredReportings: [],
8130
- _events: {},
8131
- _eventsCount: 0,
8132
- meta: {},
8133
- pendingRequests: {id: 1, deviceIeeeAddress: "0x171", sendInProgress: false},
8134
- },
8135
- {
8136
- inputClusters: [],
8137
- outputClusters: [],
8138
- ID: 2,
8139
- clusters: {},
8140
- deviceIeeeAddress: "0x171",
8141
- deviceNetworkAddress: 171,
8142
- _binds: [],
8143
- _configuredReportings: [],
8144
- _events: {},
8145
- _eventsCount: 0,
8146
- meta: {},
8147
- pendingRequests: {id: 2, deviceIeeeAddress: "0x171", sendInProgress: false},
8148
- },
8149
- {
8150
- inputClusters: [],
8151
- outputClusters: [],
8152
- ID: 3,
8153
- clusters: {},
8154
- deviceIeeeAddress: "0x171",
8155
- deviceNetworkAddress: 171,
8156
- _binds: [],
8157
- _configuredReportings: [],
8158
- _events: {},
8159
- _eventsCount: 0,
8160
- meta: {},
8161
- pendingRequests: {id: 3, deviceIeeeAddress: "0x171", sendInProgress: false},
8162
- },
8163
- {
8164
- inputClusters: [],
8165
- outputClusters: [],
8166
- ID: 4,
8167
- clusters: {},
8168
- deviceIeeeAddress: "0x171",
8169
- deviceNetworkAddress: 171,
8170
- _binds: [],
8171
- _configuredReportings: [],
8172
- _events: {},
8173
- _eventsCount: 0,
8174
- meta: {},
8175
- pendingRequests: {id: 4, deviceIeeeAddress: "0x171", sendInProgress: false},
8176
- },
8177
- {
8178
- inputClusters: [],
8179
- outputClusters: [],
8180
- ID: 5,
8181
- clusters: {},
8182
- deviceIeeeAddress: "0x171",
8183
- deviceNetworkAddress: 171,
8184
- _binds: [],
8185
- _configuredReportings: [],
8186
- _events: {},
8187
- _eventsCount: 0,
8188
- meta: {},
8189
- pendingRequests: {id: 5, deviceIeeeAddress: "0x171", sendInProgress: false},
8190
- },
8191
- {
8192
- inputClusters: [],
8193
- outputClusters: [],
8194
- ID: 6,
8195
- clusters: {},
8196
- deviceIeeeAddress: "0x171",
8197
- deviceNetworkAddress: 171,
8198
- _binds: [],
8199
- _configuredReportings: [],
8200
- _events: {},
8201
- _eventsCount: 0,
8202
- meta: {},
8203
- pendingRequests: {id: 6, deviceIeeeAddress: "0x171", sendInProgress: false},
8204
- },
8205
- ],
8206
- _events: {},
8207
- _eventsCount: 0,
8208
- _hardwareVersion: 3,
8209
- _ieeeAddr: "0x171",
8210
- _interviewState: InterviewState.Successful,
8211
- _lastSeen: Date.now(),
8212
- _manufacturerID: 1212,
8213
- _manufacturerName: "Xioami",
8214
- _modelID: "lumi.remote.b286opcn01",
8215
- _networkAddress: 171,
8216
- _powerSource: "Mains (single phase)",
8217
- _softwareBuildID: "1.01",
8218
- _stackVersion: 101,
8219
- _type: "EndDevice",
8220
- _zclVersion: 1,
8221
- _linkquality: 19,
8222
- _skipDefaultResponse: false,
8223
- meta: {},
8224
- },
8225
- endpoint: {
8226
- deviceID: 5,
8227
- inputClusters: [0, 1, 2],
8228
- outputClusters: [2],
8229
- profileID: 99,
8230
- ID: 1,
8231
- clusters: {},
8232
- deviceIeeeAddress: "0x171",
8233
- deviceNetworkAddress: 171,
8234
- _binds: [],
8235
- _configuredReportings: [],
8236
- _events: {},
8237
- _eventsCount: 0,
8238
- meta: {},
8239
- pendingRequests: {id: 1, deviceIeeeAddress: "0x171", sendInProgress: false},
8240
- },
7643
+ device: expect.objectContaining({_ieeeAddr: "0x171"}),
7644
+ endpoint: expect.objectContaining({deviceIeeeAddress: "0x171"}),
8241
7645
  data: ["mainsVoltage", 9999],
8242
7646
  linkquality: 19,
8243
7647
  groupID: 171,
@@ -8245,10 +7649,12 @@ describe("Controller", () => {
8245
7649
  meta: {
8246
7650
  zclTransactionSequenceNumber: 40,
8247
7651
  frameControl: {reservedBits: 0, frameType: 0, direction: 0, disableDefaultResponse: true, manufacturerSpecific: false},
7652
+ manufacturerCode: undefined,
7653
+ rawData: expect.any(Buffer),
8248
7654
  },
8249
7655
  };
8250
7656
  expect(events.message.length).toBe(1);
8251
- expect(deepClone(events.message[0])).toStrictEqual(expected);
7657
+ expect(events.message[0]).toStrictEqual(expected);
8252
7658
  });
8253
7659
 
8254
7660
  it("Shouldnt throw error on coordinatorCheck when adapter doesnt support backups", async () => {
@@ -8961,6 +8367,12 @@ describe("Controller", () => {
8961
8367
  });
8962
8368
 
8963
8369
  it("reads/writes to group with custom cluster when common to all members", async () => {
8370
+ interface CustomManuHerdsman {
8371
+ attributes: {customAttr: number};
8372
+ commands: never;
8373
+ commandResponses: never;
8374
+ }
8375
+
8964
8376
  await controller.start();
8965
8377
  await mockAdapterEvents.deviceJoined({networkAddress: 177, ieeeAddr: "0x177"});
8966
8378
 
@@ -8977,8 +8389,8 @@ describe("Controller", () => {
8977
8389
 
8978
8390
  group.addMember(device.getEndpoint(1)!);
8979
8391
 
8980
- await group.write("manuHerdsman", {customAttr: 15}, {});
8981
- await group.read("manuHerdsman", ["customAttr"], {});
8392
+ await group.write<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", {customAttr: 15}, {});
8393
+ await group.read<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", ["customAttr"], {});
8982
8394
 
8983
8395
  expect(mocksendZclFrameToGroup).toHaveBeenCalledTimes(2);
8984
8396
  expect(mocksendZclFrameToGroup.mock.calls[0][0]).toBe(34);
@@ -9037,22 +8449,22 @@ describe("Controller", () => {
9037
8449
  await group.read("manuHerdsman", ["customAttr"], {});
9038
8450
  }).rejects.toThrow(new Error(`Cluster with name 'manuHerdsman' does not exist`));
9039
8451
 
9040
- await group.write("manuHerdsman", {customAttr: 14}, {direction: Zcl.Direction.SERVER_TO_CLIENT});
9041
- await group.read("manuHerdsman", ["customAttr"], {direction: Zcl.Direction.SERVER_TO_CLIENT});
8452
+ await group.write<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", {customAttr: 14}, {direction: Zcl.Direction.SERVER_TO_CLIENT});
8453
+ await group.read<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", ["customAttr"], {direction: Zcl.Direction.SERVER_TO_CLIENT});
9042
8454
 
9043
8455
  expect(mocksendZclFrameToGroup).toHaveBeenCalledTimes(4);
9044
8456
 
9045
8457
  group.removeMember(device2.getEndpoint(2)!);
9046
8458
 
9047
- await group.write("manuHerdsman", {customAttr: 16}, {});
9048
- await group.read("manuHerdsman", ["customAttr"], {});
8459
+ await group.write<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", {customAttr: 16}, {});
8460
+ await group.read<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", ["customAttr"], {});
9049
8461
 
9050
8462
  expect(mocksendZclFrameToGroup).toHaveBeenCalledTimes(6);
9051
8463
 
9052
8464
  group.addMember(device2.getEndpoint(1)!);
9053
8465
 
9054
- await group.write("manuHerdsman", {customAttr: 8}, {});
9055
- await group.read("manuHerdsman", ["customAttr"], {});
8466
+ await group.write<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", {customAttr: 8}, {});
8467
+ await group.read<"manuHerdsman", CustomManuHerdsman>("manuHerdsman", ["customAttr"], {});
9056
8468
 
9057
8469
  expect(mocksendZclFrameToGroup).toHaveBeenCalledTimes(8);
9058
8470
 
@@ -9212,7 +8624,7 @@ describe("Controller", () => {
9212
8624
  type: "commandIndividualLedEffect",
9213
8625
  data: {
9214
8626
  color: 0,
9215
- duration: 255,
8627
+ duration: null,
9216
8628
  effect: 1,
9217
8629
  led: 5,
9218
8630
  level: 100,
@@ -9239,4 +8651,52 @@ describe("Controller", () => {
9239
8651
  });
9240
8652
  }).rejects.toThrow(new Error(`Cluster with name 'manuSpecificInovelli' does not exist`));
9241
8653
  });
8654
+
8655
+ it("Updates a device genBasic properties", async () => {
8656
+ await controller.start();
8657
+ expect(databaseContents().includes("0x129")).toBeFalsy();
8658
+ await mockAdapterEvents.deviceJoined({networkAddress: 129, ieeeAddr: "0x129"});
8659
+
8660
+ const device = controller.getDeviceByIeeeAddr("0x129")!;
8661
+
8662
+ expect(device.applicationVersion).toStrictEqual(2);
8663
+ expect(device.dateCode).toStrictEqual("201901");
8664
+ expect(device.hardwareVersion).toStrictEqual(3);
8665
+ expect(device.manufacturerName).toStrictEqual("KoenAndCo");
8666
+ expect(device.modelID).toStrictEqual("myModelID");
8667
+ expect(device.powerSource).toStrictEqual("Mains (single phase)");
8668
+ expect(device.softwareBuildID).toStrictEqual("1.01");
8669
+ expect(device.stackVersion).toStrictEqual(101);
8670
+ expect(device.zclVersion).toStrictEqual(1);
8671
+
8672
+ device.applicationVersion = 3;
8673
+ device.dateCode = "202501";
8674
+ device.hardwareVersion = 4;
8675
+ device.manufacturerName = "Test";
8676
+ device.modelID = "Me";
8677
+ device.powerSource = "DC Source";
8678
+ device.softwareBuildID = "2.01";
8679
+ device.stackVersion = 202;
8680
+ device.zclVersion = 2;
8681
+
8682
+ expect(device.applicationVersion).toStrictEqual(3);
8683
+ expect(device.dateCode).toStrictEqual("202501");
8684
+ expect(device.hardwareVersion).toStrictEqual(4);
8685
+ expect(device.manufacturerName).toStrictEqual("Test");
8686
+ expect(device.modelID).toStrictEqual("Me");
8687
+ expect(device.powerSource).toStrictEqual("DC Source");
8688
+ expect(device.softwareBuildID).toStrictEqual("2.01");
8689
+ expect(device.stackVersion).toStrictEqual(202);
8690
+ expect(device.zclVersion).toStrictEqual(2);
8691
+
8692
+ expect(device.genBasic.appVersion).toStrictEqual(3);
8693
+ expect(device.genBasic.dateCode).toStrictEqual("202501");
8694
+ expect(device.genBasic.hwVersion).toStrictEqual(4);
8695
+ expect(device.genBasic.manufacturerName).toStrictEqual("Test");
8696
+ expect(device.genBasic.modelId).toStrictEqual("Me");
8697
+ expect(device.genBasic.powerSource).toStrictEqual(Zcl.PowerSource["DC Source"]);
8698
+ expect(device.genBasic.swBuildId).toStrictEqual("2.01");
8699
+ expect(device.genBasic.stackVersion).toStrictEqual(202);
8700
+ expect(device.genBasic.zclVersion).toStrictEqual(2);
8701
+ });
9242
8702
  });