@willieee802/zigbee-herdsman 0.49.3 → 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 (211) 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 -4
  95. package/dist/controller/model/device.d.ts.map +1 -1
  96. package/dist/controller/model/device.js +167 -85
  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.d.ts +0 -1
  103. package/dist/controller/model/group.d.ts.map +1 -1
  104. package/dist/controller/model/group.js +14 -19
  105. package/dist/controller/model/group.js.map +1 -1
  106. package/dist/controller/touchlink.js +3 -3
  107. package/dist/controller/touchlink.js.map +1 -1
  108. package/dist/utils/timeService.js +2 -2
  109. package/dist/utils/timeService.js.map +1 -1
  110. package/dist/zspec/zcl/buffaloZcl.d.ts +3 -3
  111. package/dist/zspec/zcl/buffaloZcl.d.ts.map +1 -1
  112. package/dist/zspec/zcl/buffaloZcl.js +198 -96
  113. package/dist/zspec/zcl/buffaloZcl.js.map +1 -1
  114. package/dist/zspec/zcl/definition/cluster.d.ts +2 -2
  115. package/dist/zspec/zcl/definition/cluster.d.ts.map +1 -1
  116. package/dist/zspec/zcl/definition/cluster.js +2699 -2808
  117. package/dist/zspec/zcl/definition/cluster.js.map +1 -1
  118. package/dist/zspec/zcl/definition/clusters-types.d.ts +63 -1109
  119. package/dist/zspec/zcl/definition/clusters-types.d.ts.map +1 -1
  120. package/dist/zspec/zcl/definition/enums.d.ts +0 -1
  121. package/dist/zspec/zcl/definition/enums.d.ts.map +1 -1
  122. package/dist/zspec/zcl/definition/enums.js +0 -1
  123. package/dist/zspec/zcl/definition/enums.js.map +1 -1
  124. package/dist/zspec/zcl/definition/foundation.d.ts +306 -7
  125. package/dist/zspec/zcl/definition/foundation.d.ts.map +1 -1
  126. package/dist/zspec/zcl/definition/foundation.js +552 -207
  127. package/dist/zspec/zcl/definition/foundation.js.map +1 -1
  128. package/dist/zspec/zcl/definition/status.d.ts +21 -10
  129. package/dist/zspec/zcl/definition/status.d.ts.map +1 -1
  130. package/dist/zspec/zcl/definition/status.js +11 -0
  131. package/dist/zspec/zcl/definition/status.js.map +1 -1
  132. package/dist/zspec/zcl/definition/tstype.d.ts +57 -48
  133. package/dist/zspec/zcl/definition/tstype.d.ts.map +1 -1
  134. package/dist/zspec/zcl/utils.d.ts +7 -4
  135. package/dist/zspec/zcl/utils.d.ts.map +1 -1
  136. package/dist/zspec/zcl/utils.js +133 -240
  137. package/dist/zspec/zcl/utils.js.map +1 -1
  138. package/dist/zspec/zcl/zclFrame.d.ts +4 -4
  139. package/dist/zspec/zcl/zclFrame.d.ts.map +1 -1
  140. package/dist/zspec/zcl/zclFrame.js +19 -103
  141. package/dist/zspec/zcl/zclFrame.js.map +1 -1
  142. package/dist/zspec/zcl/zclStatusError.d.ts +1 -1
  143. package/dist/zspec/zcl/zclStatusError.d.ts.map +1 -1
  144. package/dist/zspec/zcl/zclStatusError.js +2 -2
  145. package/dist/zspec/zcl/zclStatusError.js.map +1 -1
  146. package/package.json +1 -1
  147. package/scripts/clusters-typegen.ts +44 -139
  148. package/src/adapter/adapter.ts +38 -3
  149. package/src/adapter/adapterDiscovery.ts +2 -1
  150. package/src/adapter/deconz/adapter/deconzAdapter.ts +24 -51
  151. package/src/adapter/deconz/driver/constants.ts +1 -1
  152. package/src/adapter/ember/adapter/emberAdapter.ts +23 -10
  153. package/src/adapter/ember/adapter/oneWaitress.ts +16 -6
  154. package/src/adapter/ezsp/adapter/ezspAdapter.ts +27 -48
  155. package/src/adapter/ezsp/driver/index.ts +1 -1
  156. package/src/adapter/ezsp/driver/types/index.ts +99 -99
  157. package/src/adapter/serialPort.ts +9 -0
  158. package/src/adapter/z-stack/adapter/adapter-backup.ts +1 -1
  159. package/src/adapter/z-stack/adapter/adapter-nv-memory.ts +1 -1
  160. package/src/adapter/z-stack/adapter/manager.ts +16 -2
  161. package/src/adapter/z-stack/adapter/tstype.ts +1 -0
  162. package/src/adapter/z-stack/adapter/zStackAdapter.ts +34 -81
  163. package/src/adapter/z-stack/constants/index.ts +1 -1
  164. package/src/adapter/z-stack/unpi/constants.ts +1 -1
  165. package/src/adapter/zboss/adapter/zbossAdapter.ts +23 -54
  166. package/src/adapter/zboss/driver.ts +8 -1
  167. package/src/adapter/zboss/uart.ts +14 -1
  168. package/src/adapter/zigate/adapter/zigateAdapter.ts +17 -48
  169. package/src/adapter/zoh/adapter/zohAdapter.ts +27 -50
  170. package/src/controller/controller.ts +12 -2
  171. package/src/controller/greenPower.ts +16 -9
  172. package/src/controller/helpers/ota.ts +37 -11
  173. package/src/controller/helpers/zclFrameConverter.ts +20 -17
  174. package/src/controller/model/device.ts +204 -97
  175. package/src/controller/model/endpoint.ts +36 -24
  176. package/src/controller/model/group.ts +14 -20
  177. package/src/controller/touchlink.ts +3 -3
  178. package/src/utils/timeService.ts +2 -2
  179. package/src/zspec/zcl/buffaloZcl.ts +226 -100
  180. package/src/zspec/zcl/definition/cluster.ts +2713 -2822
  181. package/src/zspec/zcl/definition/clusters-types.ts +80 -1135
  182. package/src/zspec/zcl/definition/enums.ts +0 -1
  183. package/src/zspec/zcl/definition/foundation.ts +703 -216
  184. package/src/zspec/zcl/definition/status.ts +22 -11
  185. package/src/zspec/zcl/definition/tstype.ts +59 -58
  186. package/src/zspec/zcl/utils.ts +137 -264
  187. package/src/zspec/zcl/zclFrame.ts +25 -130
  188. package/src/zspec/zcl/zclStatusError.ts +2 -2
  189. package/test/adapter/ember/emberAdapter.test.ts +191 -4
  190. package/test/adapter/ezsp/uart.test.ts +10 -10
  191. package/test/adapter/z-stack/adapter.test.ts +88 -32
  192. package/test/adapter/zoh/zohAdapter.test.ts +4 -4
  193. package/test/controller.test.ts +822 -248
  194. package/test/device-ota.test.ts +141 -16
  195. package/test/device.test.ts +731 -0
  196. package/test/requests.bench.ts +2 -0
  197. package/test/zcl.test.ts +70 -95
  198. package/test/zspec/zcl/buffalo.test.ts +251 -11
  199. package/test/zspec/zcl/foundation.test.ts +990 -0
  200. package/test/zspec/zcl/frame.test.ts +84 -69
  201. package/test/zspec/zcl/utils.test.ts +105 -81
  202. package/tsconfig.json +0 -1
  203. package/scripts/check-clusters-changes.ts +0 -328
  204. package/scripts/clusters-changes.log +0 -584
  205. package/scripts/utils.ts +0 -88
  206. package/scripts/zap-update-clusters-report.json +0 -303
  207. package/scripts/zap-update-clusters.ts +0 -1520
  208. package/scripts/zap-update-types.ts +0 -707
  209. package/scripts/zap-xml-clusters-overrides-data.ts +0 -52
  210. package/scripts/zap-xml-clusters-overrides.ts +0 -400
  211. package/scripts/zap-xml-types.ts +0 -146
@@ -1,17 +1,20 @@
1
+ import assert from "node:assert";
1
2
  import "../../utils/patchBigIntSerialization";
2
3
 
3
4
  import {BuffaloZcl} from "./buffaloZcl";
4
5
  import type {TClusterPayload, TFoundationPayload} from "./definition/clusters-types";
5
- import {BuffaloZclDataType, DataType, Direction, FrameType, ParameterCondition} from "./definition/enums";
6
- import type {FoundationCommandName} from "./definition/foundation";
6
+ import {BuffaloZclDataType, type DataType, Direction, FrameType, ParameterCondition} from "./definition/enums";
7
+ import type {FoundationCommandName, FoundationDefinition} from "./definition/foundation";
8
+ import {Status} from "./definition/status";
7
9
  import type {BuffaloZclOptions, Cluster, ClusterName, Command, CustomClusters, Parameter} from "./definition/tstype";
8
10
  import * as Utils from "./utils";
9
11
  import {ZclHeader} from "./zclHeader";
12
+ import {ZclStatusError} from "./zclStatusError";
10
13
 
11
14
  // biome-ignore lint/suspicious/noExplicitAny: API
12
15
  type ZclPayload = any;
13
16
 
14
- const ListTypes: number[] = [
17
+ const LIST_TYPES: readonly (DataType | BuffaloZclDataType)[] = [
15
18
  BuffaloZclDataType.LIST_UINT8,
16
19
  BuffaloZclDataType.LIST_UINT16,
17
20
  BuffaloZclDataType.LIST_UINT24,
@@ -23,9 +26,9 @@ export class ZclFrame {
23
26
  public readonly header: ZclHeader;
24
27
  public readonly payload: ZclPayload;
25
28
  public readonly cluster: Cluster;
26
- public readonly command: Command;
29
+ public readonly command: Command | FoundationDefinition;
27
30
 
28
- private constructor(header: ZclHeader, payload: ZclPayload, cluster: Cluster, command: Command) {
31
+ private constructor(header: ZclHeader, payload: ZclPayload, cluster: Cluster, command: Command | FoundationDefinition) {
29
32
  this.header = header;
30
33
  this.payload = payload;
31
34
  this.cluster = cluster;
@@ -45,21 +48,21 @@ export class ZclFrame {
45
48
  disableDefaultResponse: boolean,
46
49
  manufacturerCode: number | undefined,
47
50
  transactionSequenceNumber: number,
48
- commandKey: number | string | Command,
51
+ commandKey: number | string | Command | FoundationDefinition,
49
52
  clusterKey: number | string | Cluster,
50
53
  payload: ZclPayload,
51
54
  customClusters: CustomClusters,
52
55
  reservedBits = 0,
53
56
  ): ZclFrame {
54
57
  const cluster = typeof clusterKey === "object" ? clusterKey : Utils.getCluster(clusterKey, manufacturerCode, customClusters);
55
- const command: Command =
58
+ const command: Command | FoundationDefinition =
56
59
  typeof commandKey === "object"
57
60
  ? commandKey
58
61
  : frameType === FrameType.GLOBAL
59
62
  ? Utils.getGlobalCommand(commandKey)
60
63
  : direction === Direction.CLIENT_TO_SERVER
61
- ? cluster.getCommand(commandKey)
62
- : cluster.getCommandResponse(commandKey);
64
+ ? Utils.getClusterCommand(cluster, commandKey)
65
+ : Utils.getClusterCommandResponse(cluster, commandKey);
63
66
 
64
67
  const header = new ZclHeader(
65
68
  {reservedBits, frameType, direction, disableDefaultResponse, manufacturerSpecific: manufacturerCode != null},
@@ -87,45 +90,14 @@ export class ZclFrame {
87
90
  }
88
91
 
89
92
  private writePayloadGlobal(buffalo: BuffaloZcl): void {
90
- const command = Utils.getFoundationCommand(this.command.ID);
93
+ assert("write" in this.command);
91
94
 
92
- if (command.parseStrategy === "repetitive") {
93
- for (const entry of this.payload) {
94
- for (const parameter of command.parameters) {
95
- const options: BuffaloZclOptions = {};
96
-
97
- if (!ZclFrame.conditionsValid(parameter, entry, undefined)) {
98
- continue;
99
- }
100
-
101
- if (parameter.type === BuffaloZclDataType.USE_DATA_TYPE && typeof entry.dataType === "number") {
102
- // We need to grab the dataType to parse useDataType
103
- options.dataType = entry.dataType;
104
- }
105
-
106
- buffalo.write(parameter.type, entry[parameter.name], options);
107
- }
108
- }
109
- } else if (command.parseStrategy === "flat") {
110
- for (const parameter of command.parameters) {
111
- buffalo.write(parameter.type, this.payload[parameter.name], {});
112
- }
113
- } else {
114
- if (command.parseStrategy === "oneof") {
115
- if (Utils.isFoundationDiscoverRsp(command.ID)) {
116
- buffalo.writeUInt8(this.payload.discComplete);
117
-
118
- for (const entry of this.payload.attrInfos) {
119
- for (const parameter of command.parameters) {
120
- buffalo.write(parameter.type, entry[parameter.name], {});
121
- }
122
- }
123
- }
124
- }
125
- }
95
+ this.command.write(buffalo, this.payload);
126
96
  }
127
97
 
128
98
  private writePayloadCluster(buffalo: BuffaloZcl): void {
99
+ assert("parameters" in this.command);
100
+
129
101
  for (const parameter of this.command.parameters) {
130
102
  if (!ZclFrame.conditionsValid(parameter, this.payload, undefined)) {
131
103
  continue;
@@ -159,11 +131,11 @@ export class ZclFrame {
159
131
 
160
132
  const buffalo = new BuffaloZcl(buffer, header.length);
161
133
  const cluster = Utils.getCluster(clusterID, header.manufacturerCode, customClusters);
162
- const command: Command = header.isGlobal
134
+ const command: Command | FoundationDefinition = header.isGlobal
163
135
  ? Utils.getGlobalCommand(header.commandIdentifier)
164
136
  : header.frameControl.direction === Direction.CLIENT_TO_SERVER
165
- ? cluster.getCommand(header.commandIdentifier)
166
- : cluster.getCommandResponse(header.commandIdentifier);
137
+ ? Utils.getClusterCommand(cluster, header.commandIdentifier)
138
+ : Utils.getClusterCommandResponse(cluster, header.commandIdentifier);
167
139
  const payload = ZclFrame.parsePayload(header, cluster, buffalo);
168
140
 
169
141
  return new ZclFrame(header, payload, cluster, command);
@@ -184,8 +156,8 @@ export class ZclFrame {
184
156
  private static parsePayloadCluster(header: ZclHeader, cluster: Cluster, buffalo: BuffaloZcl): ZclPayload {
185
157
  const command =
186
158
  header.frameControl.direction === Direction.CLIENT_TO_SERVER
187
- ? cluster.getCommand(header.commandIdentifier)
188
- : cluster.getCommandResponse(header.commandIdentifier);
159
+ ? Utils.getClusterCommand(cluster, header.commandIdentifier)
160
+ : Utils.getClusterCommandResponse(cluster, header.commandIdentifier);
189
161
  const payload: ZclPayload = {};
190
162
 
191
163
  for (const parameter of command.parameters) {
@@ -195,7 +167,7 @@ export class ZclFrame {
195
167
  continue;
196
168
  }
197
169
 
198
- if (ListTypes.includes(parameter.type)) {
170
+ if (LIST_TYPES.includes(parameter.type)) {
199
171
  const lengthParameter = command.parameters[command.parameters.indexOf(parameter) - 1];
200
172
  const length = payload[lengthParameter.name];
201
173
 
@@ -208,7 +180,7 @@ export class ZclFrame {
208
180
  const valueToProcess = buffalo.read(parameter.type, options);
209
181
  payload[parameter.name] = Utils.processParameterRead(parameter, valueToProcess);
210
182
  } catch (error) {
211
- throw new Error(`Cannot parse '${command.name}:${parameter.name}' (${(error as Error).message})`);
183
+ throw new ZclStatusError(Status.INVALID_FIELD, `${cluster.name}:${command.name}:${parameter.name} (${(error as Error).message})`);
212
184
  }
213
185
  }
214
186
 
@@ -218,78 +190,7 @@ export class ZclFrame {
218
190
  private static parsePayloadGlobal(header: ZclHeader, buffalo: BuffaloZcl): ZclPayload {
219
191
  const command = Utils.getFoundationCommand(header.commandIdentifier);
220
192
 
221
- if (command.parseStrategy === "repetitive") {
222
- const payload = [];
223
-
224
- while (buffalo.isMore()) {
225
- // biome-ignore lint/suspicious/noExplicitAny: API
226
- const entry: {[s: string]: any} = {};
227
-
228
- for (const parameter of command.parameters) {
229
- const options: BuffaloZclOptions = {};
230
-
231
- if (!ZclFrame.conditionsValid(parameter, entry, buffalo.getBuffer().length - buffalo.getPosition())) {
232
- continue;
233
- }
234
-
235
- if (parameter.type === BuffaloZclDataType.USE_DATA_TYPE && typeof entry.dataType === "number") {
236
- // We need to grab the dataType to parse useDataType
237
- options.dataType = entry.dataType;
238
-
239
- if (entry.dataType === DataType.CHAR_STR && entry.attrId === 65281) {
240
- // [workaround] parse char str as Xiaomi struct
241
- options.dataType = BuffaloZclDataType.MI_STRUCT;
242
- }
243
- }
244
-
245
- entry[parameter.name] = buffalo.read(parameter.type, options);
246
-
247
- // TODO: not needed, but temp workaroudn to make payload equal to that of zcl-packet
248
- // XXX: is this still needed?
249
- if (parameter.type === BuffaloZclDataType.USE_DATA_TYPE && entry.dataType === DataType.STRUCT) {
250
- entry.structElms = entry.attrData;
251
- entry.numElms = entry.attrData.length;
252
- }
253
- }
254
-
255
- payload.push(entry);
256
- }
257
-
258
- return payload;
259
- }
260
-
261
- if (command.parseStrategy === "flat") {
262
- // biome-ignore lint/suspicious/noExplicitAny: API
263
- const payload: {[s: string]: any} = {};
264
-
265
- for (const parameter of command.parameters) {
266
- payload[parameter.name] = buffalo.read(parameter.type, {});
267
- }
268
-
269
- return payload;
270
- }
271
-
272
- if (command.parseStrategy === "oneof") {
273
- if (Utils.isFoundationDiscoverRsp(command.ID)) {
274
- // biome-ignore lint/suspicious/noExplicitAny: API
275
- const payload: {discComplete: number; attrInfos: {[k: string]: any}[]} = {
276
- discComplete: buffalo.readUInt8(),
277
- attrInfos: [],
278
- };
279
-
280
- while (buffalo.isMore()) {
281
- const entry: (typeof payload.attrInfos)[number] = {};
282
-
283
- for (const parameter of command.parameters) {
284
- entry[parameter.name] = buffalo.read(parameter.type, {});
285
- }
286
-
287
- payload.attrInfos.push(entry);
288
- }
289
-
290
- return payload;
291
- }
292
- }
193
+ return command.parse(buffalo);
293
194
  }
294
195
 
295
196
  /**
@@ -332,12 +233,6 @@ export class ZclFrame {
332
233
  }
333
234
  break;
334
235
  }
335
- case ParameterCondition.DATA_TYPE_CLASS_EQUAL: {
336
- if (Utils.getDataTypeClass(entry.dataType) !== condition.value) {
337
- return false;
338
- }
339
- break;
340
- }
341
236
  case ParameterCondition.FIELD_GT: {
342
237
  /*if (condition.reversed) {
343
238
  if (entry[condition.field] >= condition.value) {
@@ -379,5 +274,5 @@ export interface TFoundationZclFrame<Co extends string> {
379
274
  readonly header: ZclHeader;
380
275
  readonly payload: TFoundationPayload<Co>;
381
276
  readonly cluster: Cluster;
382
- readonly command: Command;
277
+ readonly command: FoundationDefinition;
383
278
  }
@@ -3,8 +3,8 @@ import {Status} from "./definition/status";
3
3
  export class ZclStatusError extends Error {
4
4
  public code: Status;
5
5
 
6
- constructor(code: Status) {
7
- super(`Status '${Status[code]}'`);
6
+ constructor(code: Status, details?: string) {
7
+ super(`Status '${Status[code]}'${details ? ` ${details}` : ""}`);
8
8
  this.code = code;
9
9
  }
10
10
  }
@@ -2284,7 +2284,7 @@ describe("Ember Adapter Layer", () => {
2284
2284
  });
2285
2285
 
2286
2286
  it("Adapter impl: waitFor", () => {
2287
- const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, 10, 0, 1, 15000);
2287
+ const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, 10, 0, 1, undefined, 15000);
2288
2288
  const spyCancel = vi.spyOn(waiter, "cancel");
2289
2289
 
2290
2290
  expect(typeof waiter.cancel).toStrictEqual("function");
@@ -2295,6 +2295,80 @@ describe("Ember Adapter Layer", () => {
2295
2295
  expect(spyCancel).toHaveReturned();
2296
2296
  });
2297
2297
 
2298
+ it("Adapter impl: waitFor resolves on cmd", async () => {
2299
+ const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, 10, 0, 1, undefined, 15000);
2300
+ const spyCancel = vi.spyOn(waiter, "cancel");
2301
+
2302
+ expect(typeof waiter.cancel).toStrictEqual("function");
2303
+ expect(waiter.promise).toBeDefined();
2304
+
2305
+ const messageContents = Zcl.Frame.create(
2306
+ Zcl.FrameType.GLOBAL,
2307
+ Zcl.Direction.SERVER_TO_CLIENT,
2308
+ true,
2309
+ undefined,
2310
+ 10,
2311
+ "readRsp",
2312
+ "genBasic",
2313
+ [{attrId: 0, status: 0, dataType: Zcl.DataType.UINT8, attrData: 8}],
2314
+ {},
2315
+ ).toBuffer();
2316
+ const expected = {
2317
+ clusterID: 0,
2318
+ header: Zcl.Header.fromBuffer(messageContents),
2319
+ address: 1234,
2320
+ data: messageContents,
2321
+ endpoint: 1,
2322
+ linkquality: 243,
2323
+ groupID: 0,
2324
+ wasBroadcast: false,
2325
+ destinationEndpoint: 1,
2326
+ };
2327
+
2328
+ // @ts-expect-error private
2329
+ adapter.oneWaitress.resolveZCL(expected);
2330
+
2331
+ await expect(waiter.promise).resolves.toStrictEqual(expected);
2332
+ expect(spyCancel).toHaveBeenCalledTimes(0);
2333
+ });
2334
+
2335
+ it("Adapter impl: waitFor resolves on specified default response", async () => {
2336
+ const waiter = adapter.waitFor(1234, 1, Zcl.FrameType.SPECIFIC, Zcl.Direction.CLIENT_TO_SERVER, undefined, 0x0019, 3, 5, 15000);
2337
+ const spyCancel = vi.spyOn(waiter, "cancel");
2338
+
2339
+ expect(typeof waiter.cancel).toStrictEqual("function");
2340
+ expect(waiter.promise).toBeDefined();
2341
+
2342
+ const messageContents = Zcl.Frame.create(
2343
+ Zcl.FrameType.GLOBAL,
2344
+ Zcl.Direction.SERVER_TO_CLIENT,
2345
+ true,
2346
+ undefined,
2347
+ 99,
2348
+ "defaultRsp",
2349
+ "genOta",
2350
+ {cmdId: 5, statusCode: Zcl.Status.MALFORMED_COMMAND},
2351
+ {},
2352
+ ).toBuffer();
2353
+ const expected = {
2354
+ clusterID: 0x0019,
2355
+ header: Zcl.Header.fromBuffer(messageContents),
2356
+ address: 1234,
2357
+ data: messageContents,
2358
+ endpoint: 1,
2359
+ linkquality: 243,
2360
+ groupID: 0,
2361
+ wasBroadcast: false,
2362
+ destinationEndpoint: 1,
2363
+ };
2364
+
2365
+ // @ts-expect-error private
2366
+ adapter.oneWaitress.resolveZCL(expected);
2367
+
2368
+ await expect(waiter.promise).resolves.toStrictEqual(expected);
2369
+ expect(spyCancel).toHaveBeenCalledTimes(0);
2370
+ });
2371
+
2298
2372
  it("Adapter impl: permitJoin on all", async () => {
2299
2373
  const spyResolveEvent = vi.spyOn(
2300
2374
  // @ts-expect-error private
@@ -3062,7 +3136,7 @@ describe("Ember Adapter Layer", () => {
3062
3136
  ]);
3063
3137
  });
3064
3138
 
3065
- it("Adapter impl: sendZclFrameToEndpoint with default response", async () => {
3139
+ it("Adapter impl: sendZclFrameToEndpoint with default response to global", async () => {
3066
3140
  const networkAddress: NodeId = 1234;
3067
3141
  const endpoint: number = 3;
3068
3142
  const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
@@ -3087,8 +3161,17 @@ describe("Ember Adapter Layer", () => {
3087
3161
  sequence: 0, // set by stack
3088
3162
  };
3089
3163
  const lastHopLqi: number = 234;
3090
- // defaultRsp with cmdId=0, status=0
3091
- const messageContents = Buffer.from("18030b0000", "hex");
3164
+ const messageContents = Zcl.Frame.create(
3165
+ Zcl.FrameType.GLOBAL,
3166
+ Zcl.Direction.SERVER_TO_CLIENT,
3167
+ true,
3168
+ undefined,
3169
+ 3,
3170
+ "defaultRsp",
3171
+ "genBasic",
3172
+ {cmdId: 0, statusCode: Zcl.Status.SUCCESS},
3173
+ {},
3174
+ ).toBuffer();
3092
3175
 
3093
3176
  mockEzspSend.mockImplementationOnce(() => {
3094
3177
  setTimeout(async () => {
@@ -3123,6 +3206,76 @@ describe("Ember Adapter Layer", () => {
3123
3206
  expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3124
3207
  });
3125
3208
 
3209
+ it("Adapter impl: sendZclFrameToEndpoint with default response to specific", async () => {
3210
+ const networkAddress: NodeId = 1234;
3211
+ const endpoint: number = 3;
3212
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3213
+ const zclFrame = Zcl.Frame.create(
3214
+ Zcl.FrameType.SPECIFIC,
3215
+ Zcl.Direction.CLIENT_TO_SERVER,
3216
+ false,
3217
+ undefined,
3218
+ 3,
3219
+ "add",
3220
+ "genGroups",
3221
+ {groupid: 1, groupname: ""},
3222
+ {},
3223
+ );
3224
+ const apsFrame: EmberApsFrame = {
3225
+ profileId: FIXED_ENDPOINTS[0].profileId,
3226
+ clusterId: zclFrame.cluster.ID,
3227
+ sourceEndpoint,
3228
+ destinationEndpoint: endpoint,
3229
+ options: DEFAULT_APS_OPTIONS,
3230
+ groupId: 0,
3231
+ sequence: 0, // set by stack
3232
+ };
3233
+ const lastHopLqi: number = 234;
3234
+ const messageContents = Zcl.Frame.create(
3235
+ Zcl.FrameType.GLOBAL,
3236
+ Zcl.Direction.SERVER_TO_CLIENT,
3237
+ true,
3238
+ undefined,
3239
+ 3,
3240
+ "defaultRsp",
3241
+ "genGroups",
3242
+ {cmdId: 0, statusCode: Zcl.Status.UNSUPPORTED_CLUSTER},
3243
+ {},
3244
+ ).toBuffer();
3245
+
3246
+ mockEzspSend.mockImplementationOnce(() => {
3247
+ setTimeout(async () => {
3248
+ mockEzspEmitter.emit(
3249
+ "incomingMessage",
3250
+ EmberIncomingMessageType.UNICAST,
3251
+ reverseApsFrame(apsFrame),
3252
+ lastHopLqi,
3253
+ networkAddress,
3254
+ messageContents,
3255
+ );
3256
+ await flushPromises();
3257
+ }, 300);
3258
+
3259
+ return [SLStatus.OK, ++mockAPSSequence];
3260
+ });
3261
+
3262
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, false, false, sourceEndpoint);
3263
+
3264
+ await vi.advanceTimersByTimeAsync(5000);
3265
+ await expect(p).resolves.toStrictEqual({
3266
+ clusterID: apsFrame.clusterId,
3267
+ header: Zcl.Header.fromBuffer(messageContents),
3268
+ address: networkAddress,
3269
+ data: messageContents,
3270
+ endpoint: apsFrame.destinationEndpoint,
3271
+ linkquality: lastHopLqi,
3272
+ groupID: apsFrame.groupId,
3273
+ wasBroadcast: false,
3274
+ destinationEndpoint: apsFrame.sourceEndpoint,
3275
+ } as ZclPayload);
3276
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3277
+ });
3278
+
3126
3279
  it("Adapter impl: sendZclFrameToEndpoint without response", async () => {
3127
3280
  const networkAddress: NodeId = 1234;
3128
3281
  const endpoint: number = 3;
@@ -3157,6 +3310,40 @@ describe("Ember Adapter Layer", () => {
3157
3310
  expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3158
3311
  });
3159
3312
 
3313
+ it("Adapter impl: sendZclFrameToEndpoint with encryption", async () => {
3314
+ const networkAddress: NodeId = 1234;
3315
+ const endpoint: number = 232;
3316
+ const sourceEndpoint = FIXED_ENDPOINTS[0].endpoint;
3317
+ const zclFrame = Zcl.Frame.create(
3318
+ Zcl.FrameType.GLOBAL,
3319
+ Zcl.Direction.CLIENT_TO_SERVER,
3320
+ true,
3321
+ undefined,
3322
+ 3,
3323
+ "read",
3324
+ "zigbeeDirectConfiguration",
3325
+ [{attrId: 0}],
3326
+ {},
3327
+ );
3328
+
3329
+ const p = adapter.sendZclFrameToEndpoint("0x1122334455667788", networkAddress, endpoint, zclFrame, 10000, true, false, sourceEndpoint);
3330
+
3331
+ await vi.advanceTimersByTimeAsync(5000);
3332
+ await expect(p).resolves.toStrictEqual(undefined);
3333
+
3334
+ const apsFrame: EmberApsFrame = {
3335
+ profileId: FIXED_ENDPOINTS[0].profileId,
3336
+ clusterId: zclFrame.cluster.ID,
3337
+ sourceEndpoint,
3338
+ destinationEndpoint: endpoint,
3339
+ options: (DEFAULT_APS_OPTIONS & ~EmberApsOption.RETRY) | EmberApsOption.ENCRYPTION,
3340
+ groupId: 0,
3341
+ sequence: 0, // set by stack
3342
+ };
3343
+
3344
+ expect(mockEzspSend).toHaveBeenCalledWith(EmberOutgoingMessageType.DIRECT, networkAddress, apsFrame, zclFrame.toBuffer(), 0, 0);
3345
+ });
3346
+
3160
3347
  it("Adapter impl: sendZclFrameToGroup with source endpoint", async () => {
3161
3348
  const groupId: number = 32;
3162
3349
  const zclFrame = Zcl.Frame.create(Zcl.FrameType.GLOBAL, Zcl.Direction.SERVER_TO_CLIENT, true, undefined, 1, 1, 0, [{}], {});
@@ -110,18 +110,18 @@ describe("UART", () => {
110
110
  it("Send data", async () => {
111
111
  await serialDriver.connect("/dev/ttyACM0", {});
112
112
  // send 8 frames
113
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
114
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
115
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
116
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
117
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
118
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
119
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
120
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
113
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
114
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
115
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
116
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
117
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
118
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
119
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
120
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
121
121
  expect(writeBufferSpy).toHaveBeenCalledTimes(2);
122
122
  // send another 2 frame - not counted, until resolve 8 promices
123
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
124
- serialDriver.sendDATA(Buffer.from([1, 2, 3]));
123
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
124
+ void serialDriver.sendDATA(Buffer.from([1, 2, 3]));
125
125
  expect(writeBufferSpy).toHaveBeenCalledTimes(2);
126
126
  });
127
127