zigbee-clusters 2.8.0 → 2.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1005 -183
- package/package.json +1 -1
- package/scripts/generate-types.js +132 -34
package/package.json
CHANGED
|
@@ -89,7 +89,7 @@ function zclTypeToTS(dataType) {
|
|
|
89
89
|
* @param {Function} ClusterClass - Cluster class with static ATTRIBUTES/COMMANDS
|
|
90
90
|
* @returns {object} Cluster definition
|
|
91
91
|
*/
|
|
92
|
-
function parseCluster(ClusterClass) {
|
|
92
|
+
function parseCluster(ClusterClass, exportName) {
|
|
93
93
|
const clusterName = ClusterClass.NAME;
|
|
94
94
|
const clusterId = ClusterClass.ID;
|
|
95
95
|
const attributes = [];
|
|
@@ -132,7 +132,11 @@ function parseCluster(ClusterClass) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
return {
|
|
135
|
-
|
|
135
|
+
exportName,
|
|
136
|
+
clusterName,
|
|
137
|
+
clusterId,
|
|
138
|
+
attributes,
|
|
139
|
+
commands,
|
|
136
140
|
};
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -141,8 +145,9 @@ function parseCluster(ClusterClass) {
|
|
|
141
145
|
* @param {string} clusterName
|
|
142
146
|
* @returns {string}
|
|
143
147
|
*/
|
|
144
|
-
function toInterfaceName(
|
|
145
|
-
|
|
148
|
+
function toInterfaceName(cluster) {
|
|
149
|
+
if (cluster.exportName) return cluster.exportName;
|
|
150
|
+
const name = cluster.clusterName.charAt(0).toUpperCase() + cluster.clusterName.slice(1);
|
|
146
151
|
return `${name}Cluster`;
|
|
147
152
|
}
|
|
148
153
|
|
|
@@ -152,7 +157,7 @@ function toInterfaceName(clusterName) {
|
|
|
152
157
|
* @returns {string} TypeScript interface code
|
|
153
158
|
*/
|
|
154
159
|
function generateClusterInterface(cluster) {
|
|
155
|
-
const interfaceName = toInterfaceName(cluster
|
|
160
|
+
const interfaceName = toInterfaceName(cluster);
|
|
156
161
|
const lines = [];
|
|
157
162
|
|
|
158
163
|
// Generate attributes interface
|
|
@@ -172,7 +177,10 @@ function generateClusterInterface(cluster) {
|
|
|
172
177
|
if (cluster.attributes.length > 0) {
|
|
173
178
|
const attrNames = cluster.attributes.map(a => `'${a.name}'`).join(' | ');
|
|
174
179
|
lines.push(` readAttributes<K extends ${attrNames}>(attributeNames: K[], opts?: { timeout?: number }): Promise<Pick<${interfaceName}Attributes, K>>;`);
|
|
175
|
-
lines.push(`
|
|
180
|
+
lines.push(` readAttributes(attributeNames: Array<keyof ${interfaceName}Attributes | number>, opts?: { timeout?: number }): Promise<Partial<${interfaceName}Attributes> & Record<number, unknown>>;`);
|
|
181
|
+
lines.push(` writeAttributes(attributes: Partial<${interfaceName}Attributes>, opts?: { timeout?: number }): Promise<unknown>;`);
|
|
182
|
+
lines.push(` on<K extends keyof ${interfaceName}Attributes & string>(eventName: \`attr.\${K}\`, listener: (value: ${interfaceName}Attributes[K]) => void): this;`);
|
|
183
|
+
lines.push(` once<K extends keyof ${interfaceName}Attributes & string>(eventName: \`attr.\${K}\`, listener: (value: ${interfaceName}Attributes[K]) => void): this;`);
|
|
176
184
|
}
|
|
177
185
|
|
|
178
186
|
// Add command methods
|
|
@@ -188,9 +196,9 @@ function generateClusterInterface(cluster) {
|
|
|
188
196
|
const allArgsOptional = cmd.args.every(a => a.tsType === 'Buffer');
|
|
189
197
|
const argsType = `{ ${cmd.args.map(a => `${a.name}${a.tsType === 'Buffer' ? '?' : ''}: ${a.tsType}`).join('; ')} }`;
|
|
190
198
|
// If all args are optional, make the entire args object optional
|
|
191
|
-
lines.push(` ${cmd.name}(args${allArgsOptional ? '?' : ''}: ${argsType}): Promise<${returnType}>;`);
|
|
199
|
+
lines.push(` ${cmd.name}(args${allArgsOptional ? '?' : ''}: ${argsType}, opts?: ClusterCommandOptions): Promise<${returnType}>;`);
|
|
192
200
|
} else {
|
|
193
|
-
lines.push(` ${cmd.name}(): Promise<${returnType}>;`);
|
|
201
|
+
lines.push(` ${cmd.name}(opts?: ClusterCommandOptions): Promise<${returnType}>;`);
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
|
|
@@ -202,9 +210,14 @@ function generateClusterInterface(cluster) {
|
|
|
202
210
|
/**
|
|
203
211
|
* Generate the full index.d.ts file
|
|
204
212
|
* @param {object[]} clusters - Array of parsed cluster definitions
|
|
213
|
+
* @param {Array<{
|
|
214
|
+
* constantName: string;
|
|
215
|
+
* clusterId: number;
|
|
216
|
+
* clusterName: string;
|
|
217
|
+
* }>} clusterDefinitions - Array of CLUSTER definitions used to generate typed CLUSTER exports
|
|
205
218
|
* @returns {string} Complete TypeScript definitions file
|
|
206
219
|
*/
|
|
207
|
-
function generateTypesFile(clusters) {
|
|
220
|
+
function generateTypesFile(clusters, clusterDefinitions) {
|
|
208
221
|
const lines = [];
|
|
209
222
|
|
|
210
223
|
// Header
|
|
@@ -225,30 +238,47 @@ type ConstructorOptions = {
|
|
|
225
238
|
endpointDescriptors: EndpointDescriptor[];
|
|
226
239
|
sendFrame: (endpointId: number, clusterId: number, frame: Buffer) => Promise<void>;
|
|
227
240
|
};
|
|
241
|
+
|
|
242
|
+
type ClusterCommandOptions = {
|
|
243
|
+
timeout?: number;
|
|
244
|
+
waitForResponse?: boolean;
|
|
245
|
+
disableDefaultResponse?: boolean;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
type ZCLNodeConstructorInput = {
|
|
249
|
+
endpointDescriptors?: EndpointDescriptor[];
|
|
250
|
+
sendFrame: (endpointId: number, clusterId: number, frame: Buffer) => Promise<void>;
|
|
251
|
+
handleFrame?: (
|
|
252
|
+
endpointId: number,
|
|
253
|
+
clusterId: number,
|
|
254
|
+
frame: Buffer,
|
|
255
|
+
meta?: unknown
|
|
256
|
+
) => Promise<void>;
|
|
257
|
+
};
|
|
228
258
|
`);
|
|
229
259
|
|
|
230
260
|
// Base ZCLNodeCluster interface
|
|
231
261
|
lines.push(`export interface ZCLNodeCluster extends EventEmitter {
|
|
232
|
-
discoverCommandsGenerated(
|
|
262
|
+
discoverCommandsGenerated(params?: {
|
|
233
263
|
startValue?: number;
|
|
234
264
|
maxResults?: number;
|
|
235
|
-
}): Promise<number[]>;
|
|
265
|
+
}, opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
236
266
|
|
|
237
|
-
discoverCommandsReceived(
|
|
267
|
+
discoverCommandsReceived(params?: {
|
|
238
268
|
startValue?: number;
|
|
239
269
|
maxResults?: number;
|
|
240
|
-
}): Promise<number[]>;
|
|
270
|
+
}, opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
241
271
|
|
|
242
272
|
readAttributes(
|
|
243
|
-
|
|
273
|
+
attributes: Array<string | number>,
|
|
244
274
|
opts?: { timeout?: number }
|
|
245
275
|
): Promise<{ [x: string]: unknown }>;
|
|
246
276
|
|
|
247
|
-
writeAttributes(attributes?: object): Promise<
|
|
277
|
+
writeAttributes(attributes?: object, opts?: { timeout?: number }): Promise<unknown>;
|
|
248
278
|
|
|
249
|
-
configureReporting(attributes?: object): Promise<void>;
|
|
279
|
+
configureReporting(attributes?: object, opts?: { timeout?: number }): Promise<void>;
|
|
250
280
|
|
|
251
|
-
readReportingConfiguration(attributes?: (string | number)[]): Promise<{
|
|
281
|
+
readReportingConfiguration(attributes?: (string | number)[], opts?: { timeout?: number }): Promise<{
|
|
252
282
|
status: string;
|
|
253
283
|
direction: 'reported' | 'received';
|
|
254
284
|
attributeId: number;
|
|
@@ -259,11 +289,12 @@ type ConstructorOptions = {
|
|
|
259
289
|
timeoutPeriod?: number;
|
|
260
290
|
}[]>;
|
|
261
291
|
|
|
262
|
-
discoverAttributes(): Promise<(string | number)[]>;
|
|
292
|
+
discoverAttributes(opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
263
293
|
|
|
264
|
-
discoverAttributesExtended(): Promise<{
|
|
294
|
+
discoverAttributesExtended(opts?: { timeout?: number }): Promise<{
|
|
265
295
|
name?: string;
|
|
266
296
|
id: number;
|
|
297
|
+
dataTypeId: number;
|
|
267
298
|
acl: { readable: boolean; writable: boolean; reportable: boolean };
|
|
268
299
|
}[]>;
|
|
269
300
|
}
|
|
@@ -279,12 +310,48 @@ type ConstructorOptions = {
|
|
|
279
310
|
lines.push('/** Type-safe cluster registry */');
|
|
280
311
|
lines.push('export interface ClusterRegistry {');
|
|
281
312
|
for (const cluster of clusters) {
|
|
282
|
-
const interfaceName = toInterfaceName(cluster
|
|
313
|
+
const interfaceName = toInterfaceName(cluster);
|
|
283
314
|
lines.push(` ${cluster.clusterName}?: ${interfaceName};`);
|
|
284
315
|
}
|
|
285
316
|
lines.push('}');
|
|
286
317
|
lines.push('');
|
|
287
318
|
|
|
319
|
+
// Generate cluster type lookup maps for inference helpers
|
|
320
|
+
lines.push('/** Cluster type lookup by cluster NAME */');
|
|
321
|
+
lines.push('export interface ClusterTypeByName {');
|
|
322
|
+
for (const cluster of clusters) {
|
|
323
|
+
const interfaceName = toInterfaceName(cluster);
|
|
324
|
+
lines.push(` ${cluster.clusterName}: ${interfaceName};`);
|
|
325
|
+
}
|
|
326
|
+
lines.push('}');
|
|
327
|
+
lines.push('');
|
|
328
|
+
|
|
329
|
+
// Generate cluster attribute lookup maps for inference helpers
|
|
330
|
+
lines.push('/** Cluster attributes lookup by cluster NAME */');
|
|
331
|
+
lines.push('export interface ClusterAttributesByName {');
|
|
332
|
+
for (const cluster of clusters) {
|
|
333
|
+
const interfaceName = toInterfaceName(cluster);
|
|
334
|
+
const attributesInterfaceName = `${interfaceName}Attributes`;
|
|
335
|
+
const attributesType = cluster.attributes.length > 0 ? attributesInterfaceName : 'Record<string, unknown>';
|
|
336
|
+
lines.push(` ${cluster.clusterName}: ${attributesType};`);
|
|
337
|
+
}
|
|
338
|
+
lines.push('}');
|
|
339
|
+
lines.push('');
|
|
340
|
+
|
|
341
|
+
lines.push('/** Infer a typed cluster interface from a CLUSTER definition object. */');
|
|
342
|
+
lines.push('export type ClusterTypeFromDefinition<TDef extends { NAME: string; ID: number }> =');
|
|
343
|
+
lines.push(" TDef['NAME'] extends keyof ClusterTypeByName");
|
|
344
|
+
lines.push(" ? ClusterTypeByName[TDef['NAME']]");
|
|
345
|
+
lines.push(' : ZCLNodeCluster;');
|
|
346
|
+
lines.push('');
|
|
347
|
+
|
|
348
|
+
lines.push('/** Infer typed cluster attribute map from a CLUSTER definition object. */');
|
|
349
|
+
lines.push('export type ClusterAttributesFromDefinition<TDef extends { NAME: string; ID: number }> =');
|
|
350
|
+
lines.push(" TDef['NAME'] extends keyof ClusterAttributesByName");
|
|
351
|
+
lines.push(" ? ClusterAttributesByName[TDef['NAME']]");
|
|
352
|
+
lines.push(' : Record<string, unknown>;');
|
|
353
|
+
lines.push('');
|
|
354
|
+
|
|
288
355
|
// Generate endpoint type
|
|
289
356
|
lines.push(`export type ZCLNodeEndpoint = {
|
|
290
357
|
clusters: ClusterRegistry & {
|
|
@@ -303,23 +370,46 @@ export interface ZCLNode {
|
|
|
303
370
|
}
|
|
304
371
|
`);
|
|
305
372
|
|
|
306
|
-
//
|
|
307
|
-
lines.push(`
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
373
|
+
// Runtime value exports
|
|
374
|
+
lines.push(`export const ZCLNode: {
|
|
375
|
+
new (node: ZCLNodeConstructorInput): ZCLNode;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
export const CLUSTER: {
|
|
379
|
+
`);
|
|
380
|
+
for (const def of clusterDefinitions) {
|
|
381
|
+
lines.push(` ${def.constantName}: {`);
|
|
382
|
+
lines.push(' ID: number;');
|
|
383
|
+
lines.push(` NAME: '${def.clusterName}';`);
|
|
384
|
+
lines.push(' ATTRIBUTES: unknown;');
|
|
385
|
+
lines.push(' COMMANDS: unknown;');
|
|
386
|
+
lines.push(' };');
|
|
387
|
+
}
|
|
388
|
+
lines.push('};');
|
|
315
389
|
|
|
316
390
|
// Export all cluster classes
|
|
317
391
|
for (const cluster of clusters) {
|
|
318
|
-
const interfaceName = toInterfaceName(cluster
|
|
319
|
-
|
|
392
|
+
const interfaceName = toInterfaceName(cluster);
|
|
393
|
+
const exportName = cluster.exportName || interfaceName;
|
|
394
|
+
lines.push(`export const ${exportName}: {`);
|
|
395
|
+
lines.push(` new (...args: any[]): ${interfaceName};`);
|
|
396
|
+
lines.push(` ID: ${cluster.clusterId};`);
|
|
397
|
+
lines.push(` NAME: '${cluster.clusterName}';`);
|
|
398
|
+
lines.push(' ATTRIBUTES: unknown;');
|
|
399
|
+
lines.push(' COMMANDS: unknown;');
|
|
400
|
+
lines.push('};');
|
|
320
401
|
}
|
|
321
402
|
|
|
322
|
-
lines.push('
|
|
403
|
+
lines.push('');
|
|
404
|
+
lines.push('declare const _default: {');
|
|
405
|
+
lines.push(' ZCLNode: typeof ZCLNode;');
|
|
406
|
+
lines.push(' CLUSTER: typeof CLUSTER;');
|
|
407
|
+
for (const cluster of clusters) {
|
|
408
|
+
const exportName = cluster.exportName || toInterfaceName(cluster);
|
|
409
|
+
lines.push(` ${exportName}: typeof ${exportName};`);
|
|
410
|
+
}
|
|
411
|
+
lines.push('};');
|
|
412
|
+
lines.push('export default _default;');
|
|
323
413
|
|
|
324
414
|
return lines.join('\n');
|
|
325
415
|
}
|
|
@@ -338,7 +428,7 @@ function main() {
|
|
|
338
428
|
for (const [name, value] of Object.entries(clustersModule)) {
|
|
339
429
|
if (name.endsWith('Cluster') && typeof value === 'function' && value.NAME) {
|
|
340
430
|
try {
|
|
341
|
-
const cluster = parseCluster(value);
|
|
431
|
+
const cluster = parseCluster(value, name);
|
|
342
432
|
clusters.push(cluster);
|
|
343
433
|
console.log(` ✓ ${cluster.clusterName} (${cluster.attributes.length} attrs, ${cluster.commands.length} cmds)`);
|
|
344
434
|
} catch (err) {
|
|
@@ -350,8 +440,16 @@ function main() {
|
|
|
350
440
|
// Sort clusters alphabetically
|
|
351
441
|
clusters.sort((a, b) => a.clusterName.localeCompare(b.clusterName));
|
|
352
442
|
|
|
443
|
+
const clusterDefinitions = Object.entries(clustersModule.CLUSTER)
|
|
444
|
+
.map(([constantName, value]) => ({
|
|
445
|
+
constantName,
|
|
446
|
+
clusterId: value.ID,
|
|
447
|
+
clusterName: value.NAME,
|
|
448
|
+
}))
|
|
449
|
+
.sort((a, b) => a.constantName.localeCompare(b.constantName));
|
|
450
|
+
|
|
353
451
|
console.log(`\nGenerating ${OUTPUT_FILE}...`);
|
|
354
|
-
const output = generateTypesFile(clusters);
|
|
452
|
+
const output = generateTypesFile(clusters, clusterDefinitions);
|
|
355
453
|
fs.writeFileSync(OUTPUT_FILE, output);
|
|
356
454
|
|
|
357
455
|
console.log(`Done! Generated types for ${clusters.length} clusters.`);
|