zigbee-clusters 2.7.2 → 2.8.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.
- package/index.d.ts +503 -177
- package/lib/Cluster.js +39 -20
- package/package.json +1 -1
- package/scripts/generate-types.js +54 -23
package/lib/Cluster.js
CHANGED
|
@@ -403,29 +403,39 @@ class Cluster extends EventEmitter {
|
|
|
403
403
|
/**
|
|
404
404
|
* Command which reads a given set of attributes from the remote cluster.
|
|
405
405
|
* Note: do not mix regular and manufacturer specific attributes.
|
|
406
|
-
* @param {string
|
|
406
|
+
* @param {Array<string | number>} attributes - Attribute names or numeric IDs
|
|
407
407
|
* @param {object} [opts=] - Optional parameters
|
|
408
|
-
* @param {number} [opts.timeout] - Optional timeout in
|
|
409
|
-
* @returns {Promise<Object.<string, unknown>>} -
|
|
408
|
+
* @param {number} [opts.timeout] - Optional timeout in ms
|
|
409
|
+
* @returns {Promise<Object.<string, unknown>>} - Keyed by attribute name or
|
|
410
|
+
* numeric ID
|
|
410
411
|
*/
|
|
411
|
-
async readAttributes(
|
|
412
|
-
if (
|
|
413
|
-
throw new Error('Expected
|
|
412
|
+
async readAttributes(attributes, opts) {
|
|
413
|
+
if (attributes instanceof Array === false) {
|
|
414
|
+
throw new Error('Expected attributes array, as of zigbee-clusters@2.0.0 call readAttributes([\'myAttr\'])');
|
|
414
415
|
}
|
|
415
416
|
|
|
416
|
-
if (!
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
const mismatch = attributeNames.find(n => !this.constructor.attributes[n]);
|
|
420
|
-
if (mismatch) {
|
|
421
|
-
throw new TypeError(`${mismatch} is not a valid attribute of ${this.name}`);
|
|
417
|
+
if (!attributes.length) {
|
|
418
|
+
attributes = Object.keys(this.constructor.attributes);
|
|
422
419
|
}
|
|
423
420
|
|
|
424
421
|
const idToName = {};
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
422
|
+
const numericIds = new Set();
|
|
423
|
+
const attrIds = new Set();
|
|
424
|
+
for (const attr of attributes) {
|
|
425
|
+
if (typeof attr === 'number') {
|
|
426
|
+
if (!Number.isInteger(attr) || attr < 0 || attr > 0xFFFF) {
|
|
427
|
+
throw new TypeError(`${attr} is not a valid numeric attribute ID for ${this.name}, must be an integer between 0 and 0xFFFF`);
|
|
428
|
+
}
|
|
429
|
+
numericIds.add(attr);
|
|
430
|
+
if (idToName[attr] === undefined) idToName[attr] = attr;
|
|
431
|
+
attrIds.add(attr);
|
|
432
|
+
} else if (this.constructor.attributes[attr]) {
|
|
433
|
+
idToName[this.constructor.attributes[attr].id] = attr;
|
|
434
|
+
attrIds.add(this.constructor.attributes[attr].id);
|
|
435
|
+
} else {
|
|
436
|
+
throw new TypeError(`${attr} is not a valid attribute of ${this.name}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
429
439
|
|
|
430
440
|
const resultObj = {};
|
|
431
441
|
while (attrIds.size) {
|
|
@@ -433,19 +443,23 @@ class Cluster extends EventEmitter {
|
|
|
433
443
|
const manufacturerId = this._checkForManufacturerSpecificAttributes(Array.from(attrIds));
|
|
434
444
|
debug(this.logId, 'read attributes', [...attrIds], manufacturerId ? `manufacturer specific id ${manufacturerId}` : '');
|
|
435
445
|
|
|
436
|
-
const
|
|
446
|
+
const response = await super.readAttributes({
|
|
437
447
|
attributes: [...attrIds],
|
|
438
448
|
manufacturerId,
|
|
439
449
|
}, opts);
|
|
440
450
|
|
|
441
|
-
debug(this.logId, 'read attributes result',
|
|
442
|
-
const
|
|
451
|
+
debug(this.logId, 'read attributes result', response);
|
|
452
|
+
const { attributeArrayStatusDataType } = this.constructor;
|
|
453
|
+
const result = attributeArrayStatusDataType.fromBuffer(response.attributes, 0);
|
|
443
454
|
if (!result.length) break;
|
|
444
455
|
|
|
445
456
|
result.forEach(a => {
|
|
446
457
|
attrIds.delete(a.id);
|
|
447
458
|
if (a.status === 'SUCCESS') {
|
|
448
459
|
resultObj[idToName[a.id]] = a.value;
|
|
460
|
+
if (numericIds.has(a.id) && idToName[a.id] !== a.id) {
|
|
461
|
+
resultObj[a.id] = a.value;
|
|
462
|
+
}
|
|
449
463
|
}
|
|
450
464
|
});
|
|
451
465
|
}
|
|
@@ -690,8 +704,13 @@ class Cluster extends EventEmitter {
|
|
|
690
704
|
for (const attr of attributes) {
|
|
691
705
|
const attribute = this.constructor.attributesById[attr.id];
|
|
692
706
|
const discoveredAttribute = {
|
|
693
|
-
acl:
|
|
707
|
+
acl: {
|
|
708
|
+
readable: !!attr.acl.readable,
|
|
709
|
+
writable: !!attr.acl.writable,
|
|
710
|
+
reportable: !!attr.acl.reportable,
|
|
711
|
+
},
|
|
694
712
|
id: attr.id,
|
|
713
|
+
dataTypeId: attr.dataTypeId,
|
|
695
714
|
};
|
|
696
715
|
|
|
697
716
|
// If the attribute is implemented in zigbee-clusters add name
|
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,8 @@ 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>;`);
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
// Add command methods
|
|
@@ -188,9 +194,9 @@ function generateClusterInterface(cluster) {
|
|
|
188
194
|
const allArgsOptional = cmd.args.every(a => a.tsType === 'Buffer');
|
|
189
195
|
const argsType = `{ ${cmd.args.map(a => `${a.name}${a.tsType === 'Buffer' ? '?' : ''}: ${a.tsType}`).join('; ')} }`;
|
|
190
196
|
// If all args are optional, make the entire args object optional
|
|
191
|
-
lines.push(` ${cmd.name}(args${allArgsOptional ? '?' : ''}: ${argsType}): Promise<${returnType}>;`);
|
|
197
|
+
lines.push(` ${cmd.name}(args${allArgsOptional ? '?' : ''}: ${argsType}, opts?: ClusterCommandOptions): Promise<${returnType}>;`);
|
|
192
198
|
} else {
|
|
193
|
-
lines.push(` ${cmd.name}(): Promise<${returnType}>;`);
|
|
199
|
+
lines.push(` ${cmd.name}(opts?: ClusterCommandOptions): Promise<${returnType}>;`);
|
|
194
200
|
}
|
|
195
201
|
}
|
|
196
202
|
|
|
@@ -225,30 +231,47 @@ type ConstructorOptions = {
|
|
|
225
231
|
endpointDescriptors: EndpointDescriptor[];
|
|
226
232
|
sendFrame: (endpointId: number, clusterId: number, frame: Buffer) => Promise<void>;
|
|
227
233
|
};
|
|
234
|
+
|
|
235
|
+
type ClusterCommandOptions = {
|
|
236
|
+
timeout?: number;
|
|
237
|
+
waitForResponse?: boolean;
|
|
238
|
+
disableDefaultResponse?: boolean;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
type ZCLNodeConstructorInput = {
|
|
242
|
+
endpointDescriptors?: EndpointDescriptor[];
|
|
243
|
+
sendFrame: (endpointId: number, clusterId: number, frame: Buffer) => Promise<void>;
|
|
244
|
+
handleFrame?: (
|
|
245
|
+
endpointId: number,
|
|
246
|
+
clusterId: number,
|
|
247
|
+
frame: Buffer,
|
|
248
|
+
meta?: unknown
|
|
249
|
+
) => Promise<void>;
|
|
250
|
+
};
|
|
228
251
|
`);
|
|
229
252
|
|
|
230
253
|
// Base ZCLNodeCluster interface
|
|
231
254
|
lines.push(`export interface ZCLNodeCluster extends EventEmitter {
|
|
232
|
-
discoverCommandsGenerated(
|
|
255
|
+
discoverCommandsGenerated(params?: {
|
|
233
256
|
startValue?: number;
|
|
234
257
|
maxResults?: number;
|
|
235
|
-
}): Promise<number[]>;
|
|
258
|
+
}, opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
236
259
|
|
|
237
|
-
discoverCommandsReceived(
|
|
260
|
+
discoverCommandsReceived(params?: {
|
|
238
261
|
startValue?: number;
|
|
239
262
|
maxResults?: number;
|
|
240
|
-
}): Promise<number[]>;
|
|
263
|
+
}, opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
241
264
|
|
|
242
265
|
readAttributes(
|
|
243
|
-
|
|
266
|
+
attributes: Array<string | number>,
|
|
244
267
|
opts?: { timeout?: number }
|
|
245
268
|
): Promise<{ [x: string]: unknown }>;
|
|
246
269
|
|
|
247
|
-
writeAttributes(attributes?: object): Promise<
|
|
270
|
+
writeAttributes(attributes?: object, opts?: { timeout?: number }): Promise<unknown>;
|
|
248
271
|
|
|
249
|
-
configureReporting(attributes?: object): Promise<void>;
|
|
272
|
+
configureReporting(attributes?: object, opts?: { timeout?: number }): Promise<void>;
|
|
250
273
|
|
|
251
|
-
readReportingConfiguration(attributes?: (string | number)[]): Promise<{
|
|
274
|
+
readReportingConfiguration(attributes?: (string | number)[], opts?: { timeout?: number }): Promise<{
|
|
252
275
|
status: string;
|
|
253
276
|
direction: 'reported' | 'received';
|
|
254
277
|
attributeId: number;
|
|
@@ -259,11 +282,12 @@ type ConstructorOptions = {
|
|
|
259
282
|
timeoutPeriod?: number;
|
|
260
283
|
}[]>;
|
|
261
284
|
|
|
262
|
-
discoverAttributes(): Promise<(string | number)[]>;
|
|
285
|
+
discoverAttributes(opts?: { timeout?: number }): Promise<(string | number)[]>;
|
|
263
286
|
|
|
264
|
-
discoverAttributesExtended(): Promise<{
|
|
287
|
+
discoverAttributesExtended(opts?: { timeout?: number }): Promise<{
|
|
265
288
|
name?: string;
|
|
266
289
|
id: number;
|
|
290
|
+
dataTypeId: number;
|
|
267
291
|
acl: { readable: boolean; writable: boolean; reportable: boolean };
|
|
268
292
|
}[]>;
|
|
269
293
|
}
|
|
@@ -279,7 +303,7 @@ type ConstructorOptions = {
|
|
|
279
303
|
lines.push('/** Type-safe cluster registry */');
|
|
280
304
|
lines.push('export interface ClusterRegistry {');
|
|
281
305
|
for (const cluster of clusters) {
|
|
282
|
-
const interfaceName = toInterfaceName(cluster
|
|
306
|
+
const interfaceName = toInterfaceName(cluster);
|
|
283
307
|
lines.push(` ${cluster.clusterName}?: ${interfaceName};`);
|
|
284
308
|
}
|
|
285
309
|
lines.push('}');
|
|
@@ -306,7 +330,7 @@ export interface ZCLNode {
|
|
|
306
330
|
// Module declaration for CommonJS compatibility
|
|
307
331
|
lines.push(`declare module "zigbee-clusters" {
|
|
308
332
|
export const ZCLNode: {
|
|
309
|
-
new (
|
|
333
|
+
new (node: ZCLNodeConstructorInput): ZCLNode;
|
|
310
334
|
};
|
|
311
335
|
export const CLUSTER: {
|
|
312
336
|
[key: string]: { ID: number; NAME: string; ATTRIBUTES: unknown; COMMANDS: unknown };
|
|
@@ -315,8 +339,15 @@ export interface ZCLNode {
|
|
|
315
339
|
|
|
316
340
|
// Export all cluster classes
|
|
317
341
|
for (const cluster of clusters) {
|
|
318
|
-
const interfaceName = toInterfaceName(cluster
|
|
319
|
-
|
|
342
|
+
const interfaceName = toInterfaceName(cluster);
|
|
343
|
+
const exportName = cluster.exportName || interfaceName;
|
|
344
|
+
lines.push(` export const ${exportName}: {`);
|
|
345
|
+
lines.push(` new (...args: any[]): ${interfaceName};`);
|
|
346
|
+
lines.push(' ID: number;');
|
|
347
|
+
lines.push(' NAME: string;');
|
|
348
|
+
lines.push(' ATTRIBUTES: unknown;');
|
|
349
|
+
lines.push(' COMMANDS: unknown;');
|
|
350
|
+
lines.push(' };');
|
|
320
351
|
}
|
|
321
352
|
|
|
322
353
|
lines.push('}');
|
|
@@ -338,7 +369,7 @@ function main() {
|
|
|
338
369
|
for (const [name, value] of Object.entries(clustersModule)) {
|
|
339
370
|
if (name.endsWith('Cluster') && typeof value === 'function' && value.NAME) {
|
|
340
371
|
try {
|
|
341
|
-
const cluster = parseCluster(value);
|
|
372
|
+
const cluster = parseCluster(value, name);
|
|
342
373
|
clusters.push(cluster);
|
|
343
374
|
console.log(` ✓ ${cluster.clusterName} (${cluster.attributes.length} attrs, ${cluster.commands.length} cmds)`);
|
|
344
375
|
} catch (err) {
|