@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.
Files changed (209) hide show
  1. package/.github/dependabot.yml +0 -3
  2. package/.github/workflows/ci.yml +1 -2
  3. package/.github/workflows/release-please.yml +1 -1
  4. package/.github/workflows/typedoc.yaml +3 -3
  5. package/.release-please-manifest.json +1 -1
  6. package/CHANGELOG.md +143 -0
  7. package/biome.json +1 -1
  8. package/dist/adapter/adapter.d.ts +14 -1
  9. package/dist/adapter/adapter.d.ts.map +1 -1
  10. package/dist/adapter/adapter.js +17 -0
  11. package/dist/adapter/adapter.js.map +1 -1
  12. package/dist/adapter/adapterDiscovery.d.ts.map +1 -1
  13. package/dist/adapter/adapterDiscovery.js.map +1 -1
  14. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts +1 -3
  15. package/dist/adapter/deconz/adapter/deconzAdapter.d.ts.map +1 -1
  16. package/dist/adapter/deconz/adapter/deconzAdapter.js +14 -29
  17. package/dist/adapter/deconz/adapter/deconzAdapter.js.map +1 -1
  18. package/dist/adapter/deconz/driver/constants.d.ts +1 -1
  19. package/dist/adapter/deconz/driver/constants.d.ts.map +1 -1
  20. package/dist/adapter/ember/adapter/emberAdapter.d.ts +1 -1
  21. package/dist/adapter/ember/adapter/emberAdapter.d.ts.map +1 -1
  22. package/dist/adapter/ember/adapter/emberAdapter.js +19 -10
  23. package/dist/adapter/ember/adapter/emberAdapter.js.map +1 -1
  24. package/dist/adapter/ember/adapter/oneWaitress.d.ts +2 -0
  25. package/dist/adapter/ember/adapter/oneWaitress.d.ts.map +1 -1
  26. package/dist/adapter/ember/adapter/oneWaitress.js +13 -5
  27. package/dist/adapter/ember/adapter/oneWaitress.js.map +1 -1
  28. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts +1 -3
  29. package/dist/adapter/ezsp/adapter/ezspAdapter.d.ts.map +1 -1
  30. package/dist/adapter/ezsp/adapter/ezspAdapter.js +17 -30
  31. package/dist/adapter/ezsp/adapter/ezspAdapter.js.map +1 -1
  32. package/dist/adapter/ezsp/driver/index.d.ts +1 -1
  33. package/dist/adapter/ezsp/driver/index.d.ts.map +1 -1
  34. package/dist/adapter/ezsp/driver/index.js +1 -1
  35. package/dist/adapter/ezsp/driver/index.js.map +1 -1
  36. package/dist/adapter/ezsp/driver/types/index.d.ts +1 -1
  37. package/dist/adapter/ezsp/driver/types/index.d.ts.map +1 -1
  38. package/dist/adapter/ezsp/driver/types/index.js +3 -3
  39. package/dist/adapter/ezsp/driver/types/index.js.map +1 -1
  40. package/dist/adapter/serialPort.d.ts.map +1 -1
  41. package/dist/adapter/serialPort.js +7 -0
  42. package/dist/adapter/serialPort.js.map +1 -1
  43. package/dist/adapter/z-stack/adapter/adapter-backup.js +1 -1
  44. package/dist/adapter/z-stack/adapter/adapter-backup.js.map +1 -1
  45. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js +1 -1
  46. package/dist/adapter/z-stack/adapter/adapter-nv-memory.js.map +1 -1
  47. package/dist/adapter/z-stack/adapter/manager.d.ts.map +1 -1
  48. package/dist/adapter/z-stack/adapter/manager.js +12 -2
  49. package/dist/adapter/z-stack/adapter/manager.js.map +1 -1
  50. package/dist/adapter/z-stack/adapter/tstype.d.ts.map +1 -1
  51. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts +1 -3
  52. package/dist/adapter/z-stack/adapter/zStackAdapter.d.ts.map +1 -1
  53. package/dist/adapter/z-stack/adapter/zStackAdapter.js +20 -34
  54. package/dist/adapter/z-stack/adapter/zStackAdapter.js.map +1 -1
  55. package/dist/adapter/z-stack/constants/index.d.ts +1 -1
  56. package/dist/adapter/z-stack/constants/index.d.ts.map +1 -1
  57. package/dist/adapter/z-stack/constants/index.js +1 -1
  58. package/dist/adapter/z-stack/constants/index.js.map +1 -1
  59. package/dist/adapter/z-stack/unpi/constants.d.ts +1 -1
  60. package/dist/adapter/z-stack/unpi/constants.d.ts.map +1 -1
  61. package/dist/adapter/z-stack/unpi/constants.js +1 -1
  62. package/dist/adapter/z-stack/unpi/constants.js.map +1 -1
  63. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts +7 -8
  64. package/dist/adapter/zboss/adapter/zbossAdapter.d.ts.map +1 -1
  65. package/dist/adapter/zboss/adapter/zbossAdapter.js +12 -30
  66. package/dist/adapter/zboss/adapter/zbossAdapter.js.map +1 -1
  67. package/dist/adapter/zboss/driver.d.ts.map +1 -1
  68. package/dist/adapter/zboss/driver.js +8 -1
  69. package/dist/adapter/zboss/driver.js.map +1 -1
  70. package/dist/adapter/zboss/uart.d.ts.map +1 -1
  71. package/dist/adapter/zboss/uart.js +14 -2
  72. package/dist/adapter/zboss/uart.js.map +1 -1
  73. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts +1 -3
  74. package/dist/adapter/zigate/adapter/zigateAdapter.d.ts.map +1 -1
  75. package/dist/adapter/zigate/adapter/zigateAdapter.js +8 -29
  76. package/dist/adapter/zigate/adapter/zigateAdapter.js.map +1 -1
  77. package/dist/adapter/zoh/adapter/zohAdapter.d.ts +1 -3
  78. package/dist/adapter/zoh/adapter/zohAdapter.d.ts.map +1 -1
  79. package/dist/adapter/zoh/adapter/zohAdapter.js +18 -33
  80. package/dist/adapter/zoh/adapter/zohAdapter.js.map +1 -1
  81. package/dist/controller/controller.d.ts.map +1 -1
  82. package/dist/controller/controller.js +10 -2
  83. package/dist/controller/controller.js.map +1 -1
  84. package/dist/controller/greenPower.d.ts.map +1 -1
  85. package/dist/controller/greenPower.js +15 -9
  86. package/dist/controller/greenPower.js.map +1 -1
  87. package/dist/controller/helpers/ota.d.ts +4 -4
  88. package/dist/controller/helpers/ota.d.ts.map +1 -1
  89. package/dist/controller/helpers/ota.js +28 -9
  90. package/dist/controller/helpers/ota.js.map +1 -1
  91. package/dist/controller/helpers/zclFrameConverter.d.ts.map +1 -1
  92. package/dist/controller/helpers/zclFrameConverter.js +17 -16
  93. package/dist/controller/helpers/zclFrameConverter.js.map +1 -1
  94. package/dist/controller/model/device.d.ts +14 -3
  95. package/dist/controller/model/device.d.ts.map +1 -1
  96. package/dist/controller/model/device.js +155 -68
  97. package/dist/controller/model/device.js.map +1 -1
  98. package/dist/controller/model/endpoint.d.ts +7 -3
  99. package/dist/controller/model/endpoint.d.ts.map +1 -1
  100. package/dist/controller/model/endpoint.js +34 -21
  101. package/dist/controller/model/endpoint.js.map +1 -1
  102. package/dist/controller/model/group.js +4 -4
  103. package/dist/controller/model/group.js.map +1 -1
  104. package/dist/controller/touchlink.js +3 -3
  105. package/dist/controller/touchlink.js.map +1 -1
  106. package/dist/utils/timeService.js +2 -2
  107. package/dist/utils/timeService.js.map +1 -1
  108. package/dist/zspec/zcl/buffaloZcl.d.ts +3 -3
  109. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  110. package/dist/zspec/zcl/buffaloZcl.js +198 -96
  111. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  112. package/dist/zspec/zcl/definition/cluster.d.ts +2 -2
  113. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  114. package/dist/zspec/zcl/definition/cluster.js +2699 -2808
  115. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  116. package/dist/zspec/zcl/definition/clusters-types.d.ts +63 -1109
  117. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -1
  118. package/dist/zspec/zcl/definition/enums.d.ts +0 -1
  119. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  120. package/dist/zspec/zcl/definition/enums.js +0 -1
  121. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  122. package/dist/zspec/zcl/definition/foundation.d.ts +306 -7
  123. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  124. package/dist/zspec/zcl/definition/foundation.js +552 -207
  125. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  126. package/dist/zspec/zcl/definition/status.d.ts +21 -10
  127. package/dist/zspec/zcl/definition/status.d.ts.map +1 -1
  128. package/dist/zspec/zcl/definition/status.js +11 -0
  129. package/dist/zspec/zcl/definition/status.js.map +1 -1
  130. package/dist/zspec/zcl/definition/tstype.d.ts +57 -48
  131. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  132. package/dist/zspec/zcl/utils.d.ts +7 -4
  133. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  134. package/dist/zspec/zcl/utils.js +133 -240
  135. package/dist/zspec/zcl/utils.js.map +1 -1
  136. package/dist/zspec/zcl/zclFrame.d.ts +4 -4
  137. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  138. package/dist/zspec/zcl/zclFrame.js +19 -103
  139. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  140. package/dist/zspec/zcl/zclStatusError.d.ts +1 -1
  141. package/dist/zspec/zcl/zclStatusError.d.ts.map +1 -1
  142. package/dist/zspec/zcl/zclStatusError.js +2 -2
  143. package/dist/zspec/zcl/zclStatusError.js.map +1 -1
  144. package/package.json +1 -1
  145. package/scripts/clusters-typegen.ts +44 -139
  146. package/src/adapter/adapter.ts +38 -3
  147. package/src/adapter/adapterDiscovery.ts +2 -1
  148. package/src/adapter/deconz/adapter/deconzAdapter.ts +24 -51
  149. package/src/adapter/deconz/driver/constants.ts +1 -1
  150. package/src/adapter/ember/adapter/emberAdapter.ts +23 -10
  151. package/src/adapter/ember/adapter/oneWaitress.ts +16 -6
  152. package/src/adapter/ezsp/adapter/ezspAdapter.ts +27 -48
  153. package/src/adapter/ezsp/driver/index.ts +1 -1
  154. package/src/adapter/ezsp/driver/types/index.ts +99 -99
  155. package/src/adapter/serialPort.ts +9 -0
  156. package/src/adapter/z-stack/adapter/adapter-backup.ts +1 -1
  157. package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +1 -1
  158. package/src/adapter/z-stack/adapter/manager.ts +16 -2
  159. package/src/adapter/z-stack/adapter/tstype.ts +1 -0
  160. package/src/adapter/z-stack/adapter/zStackAdapter.ts +34 -81
  161. package/src/adapter/z-stack/constants/index.ts +1 -1
  162. package/src/adapter/z-stack/unpi/constants.ts +1 -1
  163. package/src/adapter/zboss/adapter/zbossAdapter.ts +23 -54
  164. package/src/adapter/zboss/driver.ts +8 -1
  165. package/src/adapter/zboss/uart.ts +14 -1
  166. package/src/adapter/zigate/adapter/zigateAdapter.ts +17 -48
  167. package/src/adapter/zoh/adapter/zohAdapter.ts +27 -50
  168. package/src/controller/controller.ts +12 -2
  169. package/src/controller/greenPower.ts +16 -9
  170. package/src/controller/helpers/ota.ts +37 -11
  171. package/src/controller/helpers/zclFrameConverter.ts +20 -17
  172. package/src/controller/model/device.ts +192 -79
  173. package/src/controller/model/endpoint.ts +36 -24
  174. package/src/controller/model/group.ts +4 -4
  175. package/src/controller/touchlink.ts +3 -3
  176. package/src/utils/timeService.ts +2 -2
  177. package/src/zspec/zcl/buffaloZcl.ts +226 -100
  178. package/src/zspec/zcl/definition/cluster.ts +2713 -2822
  179. package/src/zspec/zcl/definition/clusters-types.ts +80 -1135
  180. package/src/zspec/zcl/definition/enums.ts +0 -1
  181. package/src/zspec/zcl/definition/foundation.ts +703 -216
  182. package/src/zspec/zcl/definition/status.ts +22 -11
  183. package/src/zspec/zcl/definition/tstype.ts +59 -58
  184. package/src/zspec/zcl/utils.ts +137 -264
  185. package/src/zspec/zcl/zclFrame.ts +25 -130
  186. package/src/zspec/zcl/zclStatusError.ts +2 -2
  187. package/test/adapter/ember/emberAdapter.test.ts +191 -4
  188. package/test/adapter/ezsp/uart.test.ts +10 -10
  189. package/test/adapter/z-stack/adapter.test.ts +88 -32
  190. package/test/adapter/zoh/zohAdapter.test.ts +4 -4
  191. package/test/controller.test.ts +822 -248
  192. package/test/device-ota.test.ts +141 -16
  193. package/test/device.test.ts +731 -0
  194. package/test/requests.bench.ts +2 -0
  195. package/test/zcl.test.ts +70 -95
  196. package/test/zspec/zcl/buffalo.test.ts +251 -11
  197. package/test/zspec/zcl/foundation.test.ts +990 -0
  198. package/test/zspec/zcl/frame.test.ts +84 -69
  199. package/test/zspec/zcl/utils.test.ts +105 -81
  200. package/tsconfig.json +0 -1
  201. package/scripts/check-clusters-changes.ts +0 -328
  202. package/scripts/clusters-changes.log +0 -584
  203. package/scripts/utils.ts +0 -88
  204. package/scripts/zap-update-clusters-report.json +0 -303
  205. package/scripts/zap-update-clusters.ts +0 -1520
  206. package/scripts/zap-update-types.ts +0 -707
  207. package/scripts/zap-xml-clusters-overrides-data.ts +0 -52
  208. package/scripts/zap-xml-clusters-overrides.ts +0 -400
  209. package/scripts/zap-xml-types.ts +0 -146
@@ -1,5 +1,4 @@
1
1
  import {Buffalo} from "../../buffalo";
2
- import {logger} from "../../utils/logger";
3
2
  import {isNumberArray} from "../../utils/utils";
4
3
  import {ZCL_TYPE_INVALID_BY_TYPE, ZclType} from "./definition/datatypes";
5
4
  import {BuffaloZclDataType, DataType, StructuredIndicatorType} from "./definition/enums";
@@ -8,11 +7,16 @@ import type {
8
7
  ExtensionFieldSet,
9
8
  Gpd,
10
9
  GpdAttributeReport,
10
+ GpdAttributeReporting,
11
11
  GpdChannelConfiguration,
12
12
  GpdChannelRequest,
13
13
  GpdCommissioningReply,
14
14
  GpdCustomReply,
15
- KeyZclValue,
15
+ GpdManufAttributeReport,
16
+ GpdManufMultiClusterAttributeReport,
17
+ GpdMultiClusterAttributeReport,
18
+ GpdReadAttributeResponse,
19
+ GpdRequestAttribute,
16
20
  MiboxerZone,
17
21
  Struct,
18
22
  StructuredSelector,
@@ -24,8 +28,7 @@ import type {
24
28
  ZoneInfo,
25
29
  } from "./definition/tstype";
26
30
  import * as Utils from "./utils";
27
-
28
- const NS = "zh:zcl:buffalo";
31
+ import {getClusterAttribute} from "./utils";
29
32
 
30
33
  const UINT8_NON_VALUE = ZCL_TYPE_INVALID_BY_TYPE[ZclType.Uint8] as number;
31
34
  const UINT16_NON_VALUE = ZCL_TYPE_INVALID_BY_TYPE[ZclType.Uint16] as number;
@@ -376,7 +379,9 @@ export class BuffaloZcl extends Buffalo {
376
379
  // 0xf6: ZCL Tunneling
377
380
  }
378
381
 
379
- private readGpdFrame(options: BuffaloZclOptions): Gpd | GpdChannelRequest | GpdAttributeReport | {raw: Buffer} | Record<string, never> {
382
+ private readGpdFrame(
383
+ options: BuffaloZclOptions,
384
+ ): Gpd | GpdChannelRequest | GpdAttributeReporting | GpdRequestAttribute | GpdReadAttributeResponse | {raw: Buffer} | Record<string, never> {
380
385
  if (options.payload?.payloadSize === undefined) {
381
386
  throw new Error("Cannot read GPD_FRAME without required payload options specified");
382
387
  }
@@ -388,137 +393,258 @@ export class BuffaloZcl extends Buffalo {
388
393
  // ensure offset by options.payload.payloadSize (if any) at end of parsing to not cause issues with spec changes (until supported)
389
394
  const startPosition = this.position;
390
395
 
391
- if (options.payload.commandID === 0xe0) {
392
- // Commisioning
393
- const frame = {
394
- deviceID: this.readUInt8(),
395
- options: this.readUInt8(),
396
- extendedOptions: 0,
397
- securityKey: Buffer.alloc(16) as Buffer<ArrayBufferLike>,
398
- keyMic: 0,
399
- outgoingCounter: 0,
400
- applicationInfo: 0,
401
- manufacturerID: 0,
402
- modelID: 0,
403
- numGpdCommands: 0,
404
- gpdCommandIdList: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
405
- numServerClusters: 0,
406
- numClientClusters: 0,
407
- gpdServerClusters: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
408
- gpdClientClusters: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
409
- genericSwitchConfig: 0,
410
- currentContactStatus: 0,
411
- };
396
+ switch (options.payload.commandID) {
397
+ case 0xe0: {
398
+ // Commisioning
399
+ const frame: Gpd = {
400
+ deviceID: this.readUInt8(),
401
+ options: this.readUInt8(),
402
+ extendedOptions: 0,
403
+ securityKey: Buffer.alloc(16) as Buffer<ArrayBufferLike>,
404
+ keyMic: 0,
405
+ outgoingCounter: 0,
406
+ applicationInfo: 0,
407
+ manufacturerID: 0,
408
+ modelID: 0,
409
+ numGpdCommands: 0,
410
+ gpdCommandIdList: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
411
+ numServerClusters: 0,
412
+ numClientClusters: 0,
413
+ gpdServerClusters: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
414
+ gpdClientClusters: Buffer.alloc(0) as Buffer<ArrayBufferLike>,
415
+ genericSwitchConfig: 0,
416
+ currentContactStatus: 0,
417
+ };
418
+
419
+ if (frame.options & 0x80) {
420
+ frame.extendedOptions = this.readUInt8();
421
+ }
412
422
 
413
- if (frame.options & 0x80) {
414
- frame.extendedOptions = this.readUInt8();
415
- }
423
+ if (frame.extendedOptions & 0x20) {
424
+ frame.securityKey = this.readBuffer(16);
425
+ }
416
426
 
417
- if (frame.extendedOptions & 0x20) {
418
- frame.securityKey = this.readBuffer(16);
419
- }
427
+ if (frame.extendedOptions & 0x40) {
428
+ frame.keyMic = this.readUInt32();
429
+ }
420
430
 
421
- if (frame.extendedOptions & 0x40) {
422
- frame.keyMic = this.readUInt32();
423
- }
431
+ if (frame.extendedOptions & 0x80) {
432
+ frame.outgoingCounter = this.readUInt32();
433
+ }
424
434
 
425
- if (frame.extendedOptions & 0x80) {
426
- frame.outgoingCounter = this.readUInt32();
427
- }
435
+ if (frame.options & 0x04) {
436
+ frame.applicationInfo = this.readUInt8();
437
+ }
428
438
 
429
- if (frame.options & 0x04) {
430
- frame.applicationInfo = this.readUInt8();
431
- }
439
+ if (frame.applicationInfo & 0x01) {
440
+ frame.manufacturerID = this.readUInt16();
441
+ }
432
442
 
433
- if (frame.applicationInfo & 0x01) {
434
- frame.manufacturerID = this.readUInt16();
435
- }
443
+ if (frame.applicationInfo & 0x02) {
444
+ frame.modelID = this.readUInt16();
445
+ }
436
446
 
437
- if (frame.applicationInfo & 0x02) {
438
- frame.modelID = this.readUInt16();
439
- }
447
+ if (frame.applicationInfo & 0x04) {
448
+ frame.numGpdCommands = this.readUInt8();
449
+ frame.gpdCommandIdList = this.readBuffer(frame.numGpdCommands);
450
+ }
451
+
452
+ if (frame.applicationInfo & 0x08) {
453
+ const len = this.readUInt8();
454
+ frame.numServerClusters = len & 0xf;
455
+ frame.numClientClusters = (len >> 4) & 0xf;
456
+
457
+ frame.gpdServerClusters = this.readBuffer(2 * frame.numServerClusters);
458
+ frame.gpdClientClusters = this.readBuffer(2 * frame.numClientClusters);
459
+ }
460
+
461
+ if (frame.applicationInfo & 0x10) {
462
+ const len = this.readUInt8();
463
+
464
+ if (len >= 1) {
465
+ frame.genericSwitchConfig = this.readUInt8();
466
+ }
467
+
468
+ if (len >= 2) {
469
+ frame.currentContactStatus = this.readUInt8();
470
+ }
471
+ }
472
+
473
+ this.setPosition(startPosition + options.payload.payloadSize);
440
474
 
441
- if (frame.applicationInfo & 0x04) {
442
- frame.numGpdCommands = this.readUInt8();
443
- frame.gpdCommandIdList = this.readBuffer(frame.numGpdCommands);
475
+ return frame;
444
476
  }
477
+ case 0xe3: {
478
+ // Channel Request
479
+ const channelOpts = this.readUInt8();
445
480
 
446
- if (frame.applicationInfo & 0x08) {
447
- const len = this.readUInt8();
448
- frame.numServerClusters = len & 0xf;
449
- frame.numClientClusters = (len >> 4) & 0xf;
481
+ this.setPosition(startPosition + options.payload.payloadSize);
450
482
 
451
- frame.gpdServerClusters = this.readBuffer(2 * frame.numServerClusters);
452
- frame.gpdClientClusters = this.readBuffer(2 * frame.numClientClusters);
483
+ return {
484
+ nextChannel: channelOpts & 0xf,
485
+ nextNextChannel: channelOpts >> 4,
486
+ } satisfies GpdChannelRequest;
453
487
  }
488
+ case 0xa0: {
489
+ // Attribute Reporting
490
+ const clusterID = this.readUInt16();
491
+ const cluster = Utils.getCluster(clusterID, undefined, {});
492
+ const frame: GpdAttributeReport = {
493
+ clusterID,
494
+ clusterName: cluster.name,
495
+ attributes: {},
496
+ };
454
497
 
455
- if (frame.applicationInfo & 0x10) {
456
- const len = this.readUInt8();
498
+ while (this.position - startPosition < options.payload.payloadSize) {
499
+ const attributeId = this.readUInt16();
500
+ const dataType = this.readUInt8();
501
+ const attributeNameOrId = getClusterAttribute(cluster, attributeId, undefined)?.name ?? attributeId;
502
+ frame.attributes[attributeNameOrId] = this.read(dataType, options);
503
+ }
457
504
 
458
- if (len >= 1) {
459
- frame.genericSwitchConfig = this.readUInt8();
505
+ this.setPosition(startPosition + options.payload.payloadSize);
506
+
507
+ return frame;
508
+ }
509
+ case 0xa1: {
510
+ // Manufacturer-specific Attribute Reporting
511
+ const manufacturerCode = this.readUInt16();
512
+ const clusterID = this.readUInt16();
513
+ const cluster = Utils.getCluster(clusterID, manufacturerCode, {});
514
+ const frame: GpdManufAttributeReport = {
515
+ manufacturerCode,
516
+ clusterID,
517
+ clusterName: cluster.name,
518
+ attributes: {},
519
+ };
520
+
521
+ while (this.position - startPosition < options.payload.payloadSize) {
522
+ const attributeId = this.readUInt16();
523
+ const dataType = this.readUInt8();
524
+ // many times will fallback to ID since lots of unknown manu-specific attributes (handled in ZHC)
525
+ const attributeNameOrId = getClusterAttribute(cluster, attributeId, manufacturerCode)?.name ?? attributeId;
526
+ frame.attributes[attributeNameOrId] = this.read(dataType, options);
460
527
  }
461
528
 
462
- if (len >= 2) {
463
- frame.currentContactStatus = this.readUInt8();
529
+ this.setPosition(startPosition + options.payload.payloadSize);
530
+
531
+ return frame;
532
+ }
533
+ case 0xa2: {
534
+ // Multi-Cluster Reporting
535
+ const frame: GpdMultiClusterAttributeReport = {reports: []};
536
+
537
+ while (this.position - startPosition < options.payload.payloadSize) {
538
+ const clusterID = this.readUInt16();
539
+ const cluster = Utils.getCluster(clusterID, undefined, {});
540
+ const attributeId = this.readUInt16();
541
+ const dataType = this.readUInt8();
542
+ const attributeNameOrId = getClusterAttribute(cluster, attributeId, undefined)?.name ?? attributeId;
543
+ const attrData = this.read(dataType, options);
544
+ const report: GpdMultiClusterAttributeReport["reports"][number] = {
545
+ clusterID,
546
+ clusterName: cluster.name,
547
+ attribute: attributeNameOrId,
548
+ attrData,
549
+ };
550
+
551
+ frame.reports.push(report);
464
552
  }
465
- }
466
553
 
467
- this.setPosition(startPosition + options.payload.payloadSize);
554
+ this.setPosition(startPosition + options.payload.payloadSize);
555
+
556
+ return frame;
557
+ }
558
+ case 0xa3: {
559
+ // Manufacturer-specific Multi-Cluster Reporting
560
+ const manufacturerCode = this.readUInt16();
561
+ const frame: GpdManufMultiClusterAttributeReport = {manufacturerCode, reports: []};
562
+
563
+ while (this.position - startPosition < options.payload.payloadSize) {
564
+ const clusterID = this.readUInt16();
565
+ const cluster = Utils.getCluster(clusterID, manufacturerCode, {});
566
+ const attributeId = this.readUInt16();
567
+ const dataType = this.readUInt8();
568
+ // many times will fallback to ID since lots of unknown manu-specific attributes (handled in ZHC)
569
+ const attributeNameOrId = getClusterAttribute(cluster, attributeId, manufacturerCode)?.name ?? attributeId;
570
+ const attrData = this.read(dataType, options);
571
+ const report: GpdManufMultiClusterAttributeReport["reports"][number] = {
572
+ clusterID,
573
+ clusterName: cluster.name,
574
+ attribute: attributeNameOrId,
575
+ attrData,
576
+ };
577
+
578
+ frame.reports.push(report);
579
+ }
468
580
 
469
- return frame;
470
- }
581
+ this.setPosition(startPosition + options.payload.payloadSize);
471
582
 
472
- if (options.payload.commandID === 0xe3) {
473
- // Channel Request
474
- const channelOpts = this.readUInt8();
583
+ return frame;
584
+ }
585
+ case 0xa4: {
586
+ // Request Attributes
587
+ const frame: GpdRequestAttribute = {options: this.readUInt8(), manufacturerID: undefined, records: []};
475
588
 
476
- this.setPosition(startPosition + options.payload.payloadSize);
589
+ // ignore multi-record bit, while loop automatically handles it
477
590
 
478
- return {
479
- nextChannel: channelOpts & 0xf,
480
- nextNextChannel: channelOpts >> 4,
481
- };
482
- }
591
+ if (frame.options & 0x02) {
592
+ frame.manufacturerID = this.readUInt16();
593
+ }
483
594
 
484
- if (options.payload.commandID === 0xa1) {
485
- // Manufacturer-specific Attribute Reporting
486
- const start = this.position;
487
- const frame = {
488
- manufacturerCode: this.readUInt16(),
489
- clusterID: this.readUInt16(),
490
- attributes: {} as KeyZclValue,
491
- };
595
+ while (this.position - startPosition < options.payload.payloadSize) {
596
+ const clusterID = this.readUInt16();
597
+ const recordsLength = this.readUInt8();
598
+ const attributes = this.readListUInt16(recordsLength);
492
599
 
493
- const cluster = Utils.getCluster(frame.clusterID, frame.manufacturerCode, {});
600
+ frame.records.push({clusterID, attributes});
601
+ }
602
+
603
+ this.setPosition(startPosition + options.payload.payloadSize);
494
604
 
495
- while (this.position - start < options.payload.payloadSize) {
496
- const attributeID = this.readUInt16();
497
- const type = this.readUInt8();
498
- /* v8 ignore next */
499
- let attribute: string | undefined | number = cluster.getAttribute(attributeID)?.name;
605
+ return frame;
606
+ }
607
+ case 0xa5: {
608
+ // Read Attributes Response
609
+ const frame: GpdReadAttributeResponse = {options: this.readUInt8(), manufacturerID: undefined, records: []};
500
610
 
501
- // number type is only used when going into this if
502
- if (!attribute) {
503
- // this is spammy because of the many manufacturer-specific attributes not currently used
504
- logger.debug(`Unknown attribute ${attributeID} in cluster ${cluster.name}`, NS);
611
+ // ignore multi-record bit, while loop automatically handles it
505
612
 
506
- attribute = attributeID;
613
+ if (frame.options & 0x02) {
614
+ frame.manufacturerID = this.readUInt16();
507
615
  }
508
616
 
509
- frame.attributes[attribute] = this.read(type, options);
510
- }
617
+ while (this.position - startPosition < options.payload.payloadSize) {
618
+ const clusterID = this.readUInt16();
619
+ const cluster = Utils.getCluster(clusterID, frame.manufacturerID, {});
620
+ const recordsLength = this.readUInt8();
621
+ const record: GpdReadAttributeResponse["records"][number] = {clusterID, clusterName: cluster.name, attributes: {}};
622
+
623
+ for (let i = 0; i < recordsLength; i++) {
624
+ const attributeId = this.readUInt16();
625
+ const status = this.readUInt8();
626
+ const dataType = this.readUInt8();
627
+ const attributeNameOrId = getClusterAttribute(cluster, attributeId, frame.manufacturerID)?.name ?? attributeId;
628
+ record.attributes[attributeNameOrId] = {
629
+ status,
630
+ attrData: this.read(dataType, options),
631
+ };
632
+ }
511
633
 
512
- this.setPosition(startPosition + options.payload.payloadSize);
634
+ frame.records.push(record);
635
+ }
513
636
 
514
- return frame;
637
+ this.setPosition(startPosition + options.payload.payloadSize);
638
+
639
+ return frame;
640
+ }
515
641
  }
516
642
 
517
643
  // might contain `gppNwkAddr`, `gppGpdLink` & `mic` from ZCL cluster after this, so limit by `payloadSize`
518
644
  return {raw: this.readBuffer(options.payload.payloadSize)};
519
645
  }
520
646
 
521
- private writeStructuredSelector(value: StructuredSelector): void {
647
+ public writeStructuredSelector(value: StructuredSelector): void {
522
648
  if (value != null) {
523
649
  const indexes = value.indexes || [];
524
650
  const indicatorType = value.indicatorType || StructuredIndicatorType.Whole;
@@ -532,7 +658,7 @@ export class BuffaloZcl extends Buffalo {
532
658
  }
533
659
  }
534
660
 
535
- private readStructuredSelector(): StructuredSelector {
661
+ public readStructuredSelector(): StructuredSelector {
536
662
  /** [0-15] range */
537
663
  const indicator = this.readUInt8();
538
664