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
@@ -0,0 +1,588 @@
1
+ /* v8 ignore start */
2
+ /**
3
+ * How to run:
4
+ * ```bash
5
+ * npm i -g ts-node
6
+ * ts-node ./src/zspec/zcl/definition/clusters-typegen.ts && pnpm run check:w
7
+ * ```
8
+ * or with compiled:
9
+ * ```bash
10
+ * pnpm run prepack
11
+ * node ./dist/zspec/zcl/definition/clusters-typegen.js && pnpm run check:w
12
+ * ```
13
+ */
14
+ import {writeFileSync} from "node:fs";
15
+ import ts from "typescript";
16
+ import {isFoundationDiscoverRsp} from "../utils";
17
+ import {Clusters} from "./cluster";
18
+ import {BuffaloZclDataType, DataType} from "./enums";
19
+ import {Foundation, type FoundationCommandName, type FoundationDefinition} from "./foundation";
20
+ import {ManufacturerCode} from "./manufacturerCode";
21
+ import type {AttributeDefinition, ClusterName, CommandDefinition, ParameterDefinition} from "./tstype";
22
+
23
+ const FILENAME = "clusters-types.ts";
24
+
25
+ const file = ts.createSourceFile(FILENAME, "", ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS);
26
+ const printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed});
27
+
28
+ const emptyObject = ts.factory.createTypeReferenceNode("Record", [
29
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
30
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
31
+ ]);
32
+
33
+ const namedImports = ts.factory.createImportDeclaration(
34
+ undefined,
35
+ ts.factory.createImportClause(
36
+ true,
37
+ undefined,
38
+ ts.factory.createNamedImports([
39
+ // sorted by name
40
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ExtensionFieldSet")),
41
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("Gpd")),
42
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("GpdAttributeReport")),
43
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("GpdChannelConfiguration")),
44
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("GpdChannelRequest")),
45
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("GpdCommissioningReply")),
46
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("GpdCustomReply")),
47
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("MiboxerZone")),
48
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("Struct")),
49
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("StructuredSelector")),
50
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ThermoTransition")),
51
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("TuyaDataPointValue")),
52
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ZclArray")),
53
+ // ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ZclDate")), // XXX: currently unused
54
+ // ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ZclTimeOfDay")), // XXX: currently unused
55
+ ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier("ZoneInfo")),
56
+ ]),
57
+ ),
58
+ ts.factory.createStringLiteral("./tstype"),
59
+ undefined,
60
+ );
61
+
62
+ const getTypeFromDataType = (dataType: DataType | BuffaloZclDataType): ts.TypeNode => {
63
+ switch (dataType) {
64
+ case DataType.NO_DATA:
65
+ case DataType.UNKNOWN: {
66
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
67
+ }
68
+ case DataType.DATA56:
69
+ case DataType.BITMAP56:
70
+ case DataType.UINT56:
71
+ case DataType.DATA64:
72
+ case DataType.BITMAP64:
73
+ case DataType.UINT64:
74
+ case DataType.INT56:
75
+ case DataType.INT64: {
76
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword);
77
+ }
78
+ case DataType.OCTET_STR:
79
+ case DataType.LONG_OCTET_STR:
80
+ case DataType.SEC_KEY: {
81
+ return ts.factory.createTypeReferenceNode("Buffer");
82
+ }
83
+ case DataType.CHAR_STR:
84
+ case DataType.LONG_CHAR_STR:
85
+ case DataType.IEEE_ADDR: {
86
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
87
+ }
88
+ case DataType.ARRAY:
89
+ case DataType.SET:
90
+ case DataType.BAG: {
91
+ // mismatch on read vs write, have to union
92
+ return ts.factory.createUnionTypeNode([
93
+ ts.factory.createTypeReferenceNode("ZclArray"),
94
+ ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)),
95
+ ]);
96
+ }
97
+ case DataType.STRUCT: {
98
+ return ts.factory.createTypeReferenceNode("Struct");
99
+ }
100
+ case DataType.TOD: {
101
+ return ts.factory.createTypeReferenceNode("ZclTimeOfDay");
102
+ }
103
+ case DataType.DATE: {
104
+ return ts.factory.createTypeReferenceNode("ZclDate");
105
+ }
106
+ case BuffaloZclDataType.USE_DATA_TYPE: {
107
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
108
+ }
109
+ case BuffaloZclDataType.LIST_UINT8:
110
+ case BuffaloZclDataType.LIST_UINT16:
111
+ case BuffaloZclDataType.LIST_UINT24:
112
+ case BuffaloZclDataType.LIST_UINT32: {
113
+ return ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
114
+ }
115
+ case BuffaloZclDataType.LIST_ZONEINFO: {
116
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode("ZoneInfo"));
117
+ }
118
+ case BuffaloZclDataType.EXTENSION_FIELD_SETS: {
119
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode("ExtensionFieldSet"));
120
+ }
121
+ case BuffaloZclDataType.LIST_THERMO_TRANSITIONS: {
122
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode("ThermoTransition"));
123
+ }
124
+ case BuffaloZclDataType.BUFFER: {
125
+ return ts.factory.createTypeReferenceNode("Buffer");
126
+ }
127
+ case BuffaloZclDataType.GPD_FRAME: {
128
+ return ts.factory.createUnionTypeNode([
129
+ ts.factory.createTypeReferenceNode("Gpd"),
130
+ ts.factory.createTypeReferenceNode("GpdChannelRequest"),
131
+ ts.factory.createTypeReferenceNode("GpdAttributeReport"),
132
+ ts.factory.createTypeLiteralNode([
133
+ ts.factory.createPropertySignature(undefined, "raw", undefined, ts.factory.createTypeReferenceNode("Buffer")),
134
+ ]),
135
+ ts.factory.createTypeReferenceNode("Record<string, never>"),
136
+ ts.factory.createTypeReferenceNode("GpdCommissioningReply"),
137
+ ts.factory.createTypeReferenceNode("GpdChannelConfiguration"),
138
+ ts.factory.createTypeReferenceNode("GpdCustomReply"),
139
+ ]);
140
+ }
141
+ case BuffaloZclDataType.STRUCTURED_SELECTOR: {
142
+ return ts.factory.createTypeReferenceNode("StructuredSelector");
143
+ }
144
+ case BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES: {
145
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode("TuyaDataPointValue"));
146
+ }
147
+ case BuffaloZclDataType.LIST_MIBOXER_ZONES: {
148
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode("MiboxerZone"));
149
+ }
150
+ default: {
151
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
152
+ }
153
+ }
154
+ };
155
+
156
+ const getConditionStr = (conditions: ParameterDefinition["conditions"]): string | undefined => {
157
+ if (conditions) {
158
+ let str = ", Conditions: [";
159
+
160
+ for (const condition of conditions) {
161
+ str += `{${condition.type}`;
162
+
163
+ for (const key in condition) {
164
+ if (key === "type") {
165
+ continue;
166
+ }
167
+
168
+ str += ` ${key}=${condition[key as keyof typeof condition]}`;
169
+ }
170
+
171
+ str += "}";
172
+ }
173
+
174
+ return `${str}]`;
175
+ }
176
+ };
177
+
178
+ const addAttributes = (attributes: Readonly<Record<string, Readonly<AttributeDefinition>>>): ts.TypeNode => {
179
+ const elements: ts.PropertySignature[] = [];
180
+
181
+ for (const attributeName in attributes) {
182
+ const attribute = attributes[attributeName];
183
+
184
+ const element = ts.factory.createPropertySignature(
185
+ undefined,
186
+ attributeName,
187
+ // always optional if manuf-specific
188
+ attribute.manufacturerCode ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
189
+ getTypeFromDataType(attribute.type),
190
+ );
191
+
192
+ elements.push(element);
193
+
194
+ let comment = `* ID: ${attribute.ID} | Type: ${DataType[attribute.type] ?? BuffaloZclDataType[attribute.type]} `;
195
+
196
+ if (attribute.manufacturerCode !== undefined) {
197
+ comment += `| Specific to manufacturer: ${ManufacturerCode[attribute.manufacturerCode]} (${attribute.manufacturerCode}) `;
198
+ }
199
+
200
+ ts.addSyntheticLeadingComment(element, ts.SyntaxKind.MultiLineCommentTrivia, comment, true);
201
+ }
202
+
203
+ return elements.length > 0 ? ts.factory.createTypeLiteralNode(elements) : ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
204
+ };
205
+
206
+ const addCommands = (commands: Readonly<Record<string, Readonly<CommandDefinition>>>): ts.TypeNode => {
207
+ const elements: ts.PropertySignature[] = [];
208
+
209
+ for (const commandName in commands) {
210
+ const command = commands[commandName];
211
+ const cmdElements: ts.PropertySignature[] = [];
212
+
213
+ for (const parameter of command.parameters) {
214
+ // @ts-expect-error bad typing?
215
+ const existing = cmdElements.find((element) => element.name.escapedText === parameter.name);
216
+ const paramType = getTypeFromDataType(parameter.type);
217
+
218
+ if (!existing) {
219
+ const cmdElement = ts.factory.createPropertySignature(
220
+ undefined,
221
+ parameter.name,
222
+ parameter.conditions ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
223
+ paramType,
224
+ );
225
+ const conditionComment = getConditionStr(parameter.conditions);
226
+
227
+ cmdElements.push(cmdElement);
228
+ ts.addSyntheticLeadingComment(
229
+ cmdElement,
230
+ ts.SyntaxKind.MultiLineCommentTrivia,
231
+ `* Type: ${DataType[parameter.type] ?? BuffaloZclDataType[parameter.type]}${conditionComment ?? ""} `,
232
+ true,
233
+ );
234
+ } else if (
235
+ // @ts-expect-error bad typing?
236
+ existing.type?.typeName?.escapedText &&
237
+ // @ts-expect-error bad typing?
238
+ paramType.typeName?.escapedText &&
239
+ // @ts-expect-error bad typing?
240
+ existing.type.typeName.escapedText !== paramType.typeName.escapedText
241
+ ) {
242
+ // XXX: not currently used, untested
243
+ ts.factory.updatePropertySignature(
244
+ existing,
245
+ existing.modifiers,
246
+ existing.name,
247
+ existing.questionToken,
248
+ ts.factory.createUnionTypeNode([existing.type, paramType]),
249
+ );
250
+ } else if (!parameter.conditions) {
251
+ throw new Error(`Two or more cluster command parameters have identical name without conditions. ${JSON.stringify(parameter)}`);
252
+ }
253
+ }
254
+
255
+ const element = ts.factory.createPropertySignature(
256
+ undefined,
257
+ commandName,
258
+ undefined,
259
+ cmdElements.length > 0 ? ts.factory.createTypeLiteralNode(cmdElements) : emptyObject,
260
+ );
261
+
262
+ elements.push(element);
263
+
264
+ let comment = `* ID: ${command.ID} `;
265
+
266
+ if (command.response !== undefined) {
267
+ comment += `| Response ID: ${command.response} `;
268
+ }
269
+
270
+ ts.addSyntheticLeadingComment(element, ts.SyntaxKind.MultiLineCommentTrivia, comment, true);
271
+ }
272
+
273
+ return elements.length > 0 ? ts.factory.createTypeLiteralNode(elements) : ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword);
274
+ };
275
+
276
+ const clusterElements: ts.TypeElement[] = [];
277
+
278
+ for (const clusterName in Clusters) {
279
+ const cluster = Clusters[clusterName as ClusterName];
280
+
281
+ const attributesProp = ts.factory.createPropertySignature(undefined, "attributes", undefined, addAttributes(cluster.attributes));
282
+ const commandsProp = ts.factory.createPropertySignature(undefined, "commands", undefined, addCommands(cluster.commands));
283
+ const commandResponsesProp = ts.factory.createPropertySignature(undefined, "commandResponses", undefined, addCommands(cluster.commandsResponse));
284
+
285
+ clusterElements.push(
286
+ ts.factory.createPropertySignature(
287
+ undefined,
288
+ clusterName,
289
+ undefined,
290
+ ts.factory.createTypeLiteralNode([attributesProp, commandsProp, commandResponsesProp]),
291
+ ),
292
+ );
293
+ }
294
+
295
+ const clustersDecl = ts.factory.createInterfaceDeclaration(
296
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
297
+ "TClusters",
298
+ undefined,
299
+ undefined,
300
+ clusterElements,
301
+ );
302
+
303
+ const addParameters = (foundation: Readonly<FoundationDefinition>): ts.TypeNode => {
304
+ const elements: ts.PropertySignature[] = [];
305
+
306
+ for (const parameter of foundation.parameters) {
307
+ const element = ts.factory.createPropertySignature(
308
+ undefined,
309
+ parameter.name,
310
+ parameter.conditions ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
311
+ getTypeFromDataType(parameter.type),
312
+ );
313
+ const conditionComment = getConditionStr(parameter.conditions);
314
+
315
+ elements.push(element);
316
+
317
+ ts.addSyntheticLeadingComment(
318
+ element,
319
+ ts.SyntaxKind.MultiLineCommentTrivia,
320
+ `* Type: ${DataType[parameter.type] ?? BuffaloZclDataType[parameter.type]}${conditionComment ?? ""} `,
321
+ true,
322
+ );
323
+ }
324
+
325
+ if (elements.length === 0) {
326
+ return emptyObject;
327
+ }
328
+
329
+ switch (foundation.parseStrategy) {
330
+ case "repetitive": {
331
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeLiteralNode(elements));
332
+ }
333
+ case "flat": {
334
+ return ts.factory.createTypeLiteralNode(elements);
335
+ }
336
+ case "oneof": {
337
+ if (isFoundationDiscoverRsp(foundation.ID)) {
338
+ const discComplete = ts.factory.createPropertySignature(
339
+ undefined,
340
+ "discComplete",
341
+ undefined,
342
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
343
+ );
344
+
345
+ ts.addSyntheticLeadingComment(discComplete, ts.SyntaxKind.MultiLineCommentTrivia, `* Type: ${DataType[DataType.UINT8]} `, true);
346
+
347
+ return ts.factory.createTypeLiteralNode([
348
+ discComplete,
349
+ ts.factory.createPropertySignature(
350
+ undefined,
351
+ "attrInfos",
352
+ undefined,
353
+ ts.factory.createArrayTypeNode(ts.factory.createTypeLiteralNode(elements)),
354
+ ),
355
+ ]);
356
+ }
357
+ }
358
+ }
359
+
360
+ throw new Error(`Unknown command strategy for ${JSON.stringify(foundation)}`);
361
+ };
362
+
363
+ const foundationElements: ts.TypeElement[] = [];
364
+ const foundationRepetitiveElements: ts.TypeNode[] = [];
365
+ const foundationFlatElements: ts.TypeNode[] = [];
366
+ const foundationOneOfElements: ts.TypeNode[] = [];
367
+
368
+ for (const foundationName in Foundation) {
369
+ const foundation = Foundation[foundationName as FoundationCommandName];
370
+ const element = ts.factory.createPropertySignature(undefined, foundationName, undefined, addParameters(foundation));
371
+
372
+ foundationElements.push(element);
373
+ ts.addSyntheticLeadingComment(element, ts.SyntaxKind.MultiLineCommentTrivia, `* ID: ${foundation.ID} `, true);
374
+
375
+ const stratNode = ts.factory.createTypeReferenceNode(`"${foundationName}"`);
376
+
377
+ switch (foundation.parseStrategy) {
378
+ case "repetitive": {
379
+ foundationRepetitiveElements.push(stratNode);
380
+ break;
381
+ }
382
+ case "flat": {
383
+ foundationFlatElements.push(stratNode);
384
+ break;
385
+ }
386
+ case "oneof": {
387
+ foundationOneOfElements.push(stratNode);
388
+ break;
389
+ }
390
+ }
391
+ }
392
+
393
+ const foundationDecl = ts.factory.createInterfaceDeclaration(
394
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
395
+ "TFoundation",
396
+ undefined,
397
+ undefined,
398
+ foundationElements,
399
+ );
400
+
401
+ const foundationRepetitiveDecl = ts.factory.createTypeAliasDeclaration(
402
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
403
+ "TFoundationRepetitive",
404
+ undefined,
405
+ ts.factory.createUnionTypeNode(foundationRepetitiveElements),
406
+ );
407
+ const foundationFlatDecl = ts.factory.createTypeAliasDeclaration(
408
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
409
+ "TFoundationFlat",
410
+ undefined,
411
+ ts.factory.createUnionTypeNode(foundationFlatElements),
412
+ );
413
+ const foundationOneOfDecl = ts.factory.createTypeAliasDeclaration(
414
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
415
+ "TFoundationOneOf",
416
+ undefined,
417
+ ts.factory.createUnionTypeNode(foundationOneOfElements),
418
+ );
419
+ const clDecl = ts.factory.createTypeParameterDeclaration(
420
+ undefined,
421
+ "Cl",
422
+ ts.factory.createUnionTypeNode([
423
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
424
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
425
+ ]),
426
+ );
427
+ const coDecl = ts.factory.createTypeParameterDeclaration(
428
+ undefined,
429
+ "Co",
430
+ ts.factory.createUnionTypeNode([
431
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
432
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
433
+ ]),
434
+ );
435
+ const clusterAttributeKeysDecl = ts.factory.createTypeAliasDeclaration(
436
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
437
+ "TClusterAttributeKeys",
438
+ [clDecl],
439
+ ts.factory.createTypeReferenceNode(
440
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? (keyof ${clustersDecl.name.escapedText}[Cl]["attributes"])[] : (string | number)[];`,
441
+ ),
442
+ );
443
+ const clusterAttributesDecl = ts.factory.createTypeAliasDeclaration(
444
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
445
+ "TClusterAttributes",
446
+ [clDecl],
447
+ ts.factory.createTypeReferenceNode(
448
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? ${clustersDecl.name.escapedText}[Cl]["attributes"] : never`,
449
+ ),
450
+ );
451
+ const partialClusterAttributesDecl = ts.factory.createTypeAliasDeclaration(
452
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
453
+ "TPartialClusterAttributes",
454
+ [clDecl],
455
+ ts.factory.createTypeReferenceNode(
456
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? Partial<${clustersDecl.name.escapedText}[Cl]["attributes"]> : never`,
457
+ ),
458
+ );
459
+ const clusterCommandKeysDecl = ts.factory.createTypeAliasDeclaration(
460
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
461
+ "TClusterCommandKeys",
462
+ [clDecl],
463
+ ts.factory.createTypeReferenceNode(
464
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? (keyof ${clustersDecl.name.escapedText}[Cl]["commands"])[] : (string | number)[];`,
465
+ ),
466
+ );
467
+ const clusterCommandResponseKeysDecl = ts.factory.createTypeAliasDeclaration(
468
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
469
+ "TClusterCommandResponseKeys",
470
+ [clDecl],
471
+ ts.factory.createTypeReferenceNode(
472
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? (keyof ${clustersDecl.name.escapedText}[Cl]["commandResponses"])[] : (string | number)[];`,
473
+ ),
474
+ );
475
+ const clusterCommandsDecl = ts.factory.createTypeAliasDeclaration(
476
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
477
+ "TClusterCommands",
478
+ [clDecl],
479
+ ts.factory.createTypeReferenceNode(
480
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? ${clustersDecl.name.escapedText}[Cl]["commands"] : never`,
481
+ ),
482
+ );
483
+ const clusterCommandResponsesDecl = ts.factory.createTypeAliasDeclaration(
484
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
485
+ "TClusterCommandResponses",
486
+ [clDecl],
487
+ ts.factory.createTypeReferenceNode(
488
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? ${clustersDecl.name.escapedText}[Cl]["commandResponses"] : never`,
489
+ ),
490
+ );
491
+ const clusterCommandPayloadDecl = ts.factory.createTypeAliasDeclaration(
492
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
493
+ "TClusterCommandPayload",
494
+ [clDecl, coDecl],
495
+ ts.factory.createTypeReferenceNode(
496
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? Co extends keyof ${clustersDecl.name.escapedText}[Cl]["commands"] ? ${clustersDecl.name.escapedText}[Cl]["commands"][Co] : never : never;`,
497
+ ),
498
+ );
499
+ const clusterCommandResponsePayloadDecl = ts.factory.createTypeAliasDeclaration(
500
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
501
+ "TClusterCommandResponsePayload",
502
+ [clDecl, coDecl],
503
+ ts.factory.createTypeReferenceNode(
504
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? Co extends keyof ${clustersDecl.name.escapedText}[Cl]["commandResponses"] ? ${clustersDecl.name.escapedText}[Cl]["commandResponses"][Co] : never : never;`,
505
+ ),
506
+ );
507
+ const clusterPayloadDecl = ts.factory.createTypeAliasDeclaration(
508
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
509
+ "TClusterPayload",
510
+ [clDecl, coDecl],
511
+ ts.factory.createTypeReferenceNode(
512
+ `Cl extends keyof ${clustersDecl.name.escapedText} ? ${clustersDecl.name.escapedText}[Cl]["commands"] extends never ? ${clustersDecl.name.escapedText}[Cl]["commandResponses"] extends never ? never : Co extends keyof ${clustersDecl.name.escapedText}[Cl]["commandResponses"] ? ${clustersDecl.name.escapedText}[Cl]["commandResponses"][Co] : never : Co extends keyof ${clustersDecl.name.escapedText}[Cl]["commands"] ? ${clustersDecl.name.escapedText}[Cl]["commands"][Co] : Co extends keyof ${clustersDecl.name.escapedText}[Cl]["commandResponses"] ? ${clustersDecl.name.escapedText}[Cl]["commandResponses"][Co] : never : never;`,
513
+ ),
514
+ );
515
+ const foundationGenericPayloadDecl = ts.factory.createTypeAliasDeclaration(
516
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
517
+ "TFoundationGenericPayload",
518
+ undefined,
519
+ ts.factory.createTypeReferenceNode(`${foundationDecl.name.escapedText}[keyof ${foundationDecl.name.escapedText}]`),
520
+ );
521
+ const foundationRepetitivePayloadDecl = ts.factory.createTypeAliasDeclaration(
522
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
523
+ "TFoundationRepetitivePayload",
524
+ undefined,
525
+ ts.factory.createTypeReferenceNode(`${foundationDecl.name.escapedText}[${foundationRepetitiveDecl.name.escapedText}]`),
526
+ );
527
+ const foundationFlatPayloadDecl = ts.factory.createTypeAliasDeclaration(
528
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
529
+ "TFoundationFlatPayload",
530
+ undefined,
531
+ ts.factory.createTypeReferenceNode(`${foundationDecl.name.escapedText}[${foundationFlatDecl.name.escapedText}]`),
532
+ );
533
+ const foundationOneOfPayloadDecl = ts.factory.createTypeAliasDeclaration(
534
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
535
+ "TFoundationOneOfPayload",
536
+ undefined,
537
+ ts.factory.createTypeReferenceNode(`${foundationDecl.name.escapedText}[${foundationOneOfDecl.name.escapedText}]`),
538
+ );
539
+ const foundationPayloadDecl = ts.factory.createTypeAliasDeclaration(
540
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
541
+ "TFoundationPayload",
542
+ [coDecl],
543
+ ts.factory.createTypeReferenceNode(
544
+ `Co extends keyof ${foundationDecl.name.escapedText} ? ${foundationDecl.name.escapedText}[Co] : ${foundationGenericPayloadDecl.name.escapedText}`,
545
+ ),
546
+ );
547
+
548
+ const result = `${printer.printNode(ts.EmitHint.Unspecified, namedImports, file)}
549
+
550
+ ${printer.printNode(ts.EmitHint.Unspecified, clustersDecl, file)}
551
+
552
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationDecl, file)}
553
+
554
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationRepetitiveDecl, file)}
555
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationFlatDecl, file)}
556
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationOneOfDecl, file)}
557
+
558
+ // Clusters
559
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterAttributeKeysDecl, file)}
560
+
561
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterAttributesDecl, file)}
562
+
563
+ ${printer.printNode(ts.EmitHint.Unspecified, partialClusterAttributesDecl, file)}
564
+
565
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandKeysDecl, file)}
566
+
567
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandResponseKeysDecl, file)}
568
+
569
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandsDecl, file)}
570
+
571
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandResponsesDecl, file)}
572
+
573
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandPayloadDecl, file)}
574
+
575
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterCommandResponsePayloadDecl, file)}
576
+
577
+ ${printer.printNode(ts.EmitHint.Unspecified, clusterPayloadDecl, file)}
578
+
579
+ // Foundation
580
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationGenericPayloadDecl, file)}
581
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationRepetitivePayloadDecl, file)}
582
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationFlatPayloadDecl, file)}
583
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationOneOfPayloadDecl, file)}
584
+
585
+ ${printer.printNode(ts.EmitHint.Unspecified, foundationPayloadDecl, file)}
586
+ `;
587
+
588
+ writeFileSync(`./src/zspec/zcl/definition/${FILENAME}`, result, {encoding: "utf8"});