zigbee-clusters 2.8.1 → 2.9.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.
package/index.js CHANGED
@@ -44,6 +44,8 @@ const {
44
44
  ZCLStruct,
45
45
  } = zclTypes;
46
46
 
47
+ const { ZIGBEE_PROFILE_ID, ZIGBEE_DEVICE_ID, IAS_ZONE_TYPE } = require('./lib/constants');
48
+
47
49
  module.exports = {
48
50
  Cluster,
49
51
  BoundCluster,
@@ -55,4 +57,7 @@ module.exports = {
55
57
  ZCLStruct,
56
58
  ...Clusters,
57
59
  debug,
60
+ ZIGBEE_PROFILE_ID,
61
+ ZIGBEE_DEVICE_ID,
62
+ IAS_ZONE_TYPE,
58
63
  };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Zigbee Profile IDs from the ZCL specification.
5
+ */
6
+ const ZIGBEE_PROFILE_ID = {
7
+ INDUSTRIAL_PLANT_MONITORING: 0x0101,
8
+ HOME_AUTOMATION: 0x0104,
9
+ COMMERCIAL_BUILDING_AUTOMATION: 0x0105,
10
+ TELECOM_APPLICATIONS: 0x0107,
11
+ PERSONAL_HOME_AND_HOSPITAL_CARE: 0x0108,
12
+ ADVANCED_METERING_INITIATIVE: 0x0109,
13
+ };
14
+
15
+ /**
16
+ * Zigbee Device IDs from the ZCL specification.
17
+ */
18
+ const ZIGBEE_DEVICE_ID = {
19
+ GENERIC: {
20
+ ON_OFF_SWITCH: 0x0000,
21
+ LEVEL_CONTROL_SWITCH: 0x0001,
22
+ ON_OFF_OUTPUT: 0x0002,
23
+ LEVEL_CONTROLLABLE_OUTPUT: 0x0003,
24
+ SCENE_SELECTOR: 0x0004,
25
+ CONFIGURATION_TOOL: 0x0005,
26
+ REMOTE_CONTROL: 0x0006,
27
+ COMBINED_INTERFACE: 0x0007,
28
+ RANGE_EXTENDER: 0x0008,
29
+ MAINS_POWER_OUTLET: 0x0009,
30
+ DOOR_LOCK: 0x000a,
31
+ DOOR_LOCK_CONTROLLER: 0x000b,
32
+ SIMPLE_SENSOR: 0x000c,
33
+ CONSUMPTION_AWARENESS_DEVICE: 0x000d,
34
+ HOME_GATEWAY: 0x0050,
35
+ SMART_PLUG: 0x0051,
36
+ WHITE_GOODS: 0x0052,
37
+ METER_INTERFACE: 0x0053,
38
+ },
39
+ LIGHTING: {
40
+ ON_OFF_LIGHT: 0x0100,
41
+ DIMMABLE_LIGHT: 0x0101,
42
+ COLOR_DIMMABLE_LIGHT: 0x0102,
43
+ ON_OFF_LIGHT_SWITCH: 0x0103,
44
+ DIMMER_SWITCH: 0x0104,
45
+ COLOR_DIMMER_SWITCH: 0x0105,
46
+ LIGHT_SENSOR: 0x0106,
47
+ OCCUPANCY_SENSOR: 0x0107,
48
+ },
49
+ CLOSURES: {
50
+ SHADE: 0x0200,
51
+ SHADE_CONTROLLER: 0x0201,
52
+ WINDOW_COVERING_DEVICE: 0x0202,
53
+ WINDOW_COVERING_CONTROLLER: 0x0203,
54
+ },
55
+ HVAC: {
56
+ HEATING_COOLING_UNIT: 0x0300,
57
+ THERMOSTAT: 0x0301,
58
+ TEMPERATURE_SENSOR: 0x0302,
59
+ PUMP: 0x0303,
60
+ PUMP_CONTROLLER: 0x0304,
61
+ PRESSURE_SENSOR: 0x0305,
62
+ FLOW_SENSOR: 0x0306,
63
+ },
64
+ INTRUDER_ALARM_SYSTEMS: {
65
+ IAS_CONTROL_INDICATING_EQUIPMENT: 0x0400,
66
+ IAS_ANCILLARY_CONTROL_EQUIPMENT: 0x0401,
67
+ IAS_ZONE: 0x0402,
68
+ IAS_WARNING_DEVICE: 0x0403,
69
+ },
70
+ };
71
+
72
+ /**
73
+ * IAS Zone Type values from the ZCL specification (IAS Zone cluster, zoneType attribute).
74
+ */
75
+ const IAS_ZONE_TYPE = {
76
+ STANDARD_CIE: 0x0000,
77
+ MOTION_SENSOR: 0x000d,
78
+ CONTACT_SWITCH: 0x0015,
79
+ FIRE_SENSOR: 0x0028,
80
+ WATER_SENSOR: 0x002a,
81
+ CARBON_MONOXIDE_SENSOR: 0x002b,
82
+ PERSONAL_EMERGENCY_DEVICE: 0x002c,
83
+ VIBRATION_MOVEMENT_SENSOR: 0x002d,
84
+ REMOTE_CONTROL: 0x010f,
85
+ KEY_FOB: 0x0115,
86
+ KEYPAD: 0x021d,
87
+ STANDARD_WARNING_DEVICE: 0x0225,
88
+ GLASS_BREAK_SENSOR: 0x0226,
89
+ SECURITY_REPEATER: 0x0229,
90
+ INVALID_ZONE_TYPE: 0xffff,
91
+ };
92
+
93
+ module.exports = {
94
+ ZIGBEE_PROFILE_ID,
95
+ ZIGBEE_DEVICE_ID,
96
+ IAS_ZONE_TYPE,
97
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-clusters",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
4
4
  "description": "Zigbee Cluster Library for Node.js",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -179,6 +179,8 @@ function generateClusterInterface(cluster) {
179
179
  lines.push(` readAttributes<K extends ${attrNames}>(attributeNames: K[], opts?: { timeout?: number }): Promise<Pick<${interfaceName}Attributes, K>>;`);
180
180
  lines.push(` readAttributes(attributeNames: Array<keyof ${interfaceName}Attributes | number>, opts?: { timeout?: number }): Promise<Partial<${interfaceName}Attributes> & Record<number, unknown>>;`);
181
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;`);
182
184
  }
183
185
 
184
186
  // Add command methods
@@ -208,9 +210,29 @@ function generateClusterInterface(cluster) {
208
210
  /**
209
211
  * Generate the full index.d.ts file
210
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
211
218
  * @returns {string} Complete TypeScript definitions file
212
219
  */
213
- function generateTypesFile(clusters) {
220
+ function generateConstantObject(obj, indent = 2) {
221
+ const lines = [];
222
+ const pad = ' '.repeat(indent);
223
+ for (const [key, value] of Object.entries(obj)) {
224
+ if (typeof value === 'object' && value !== null) {
225
+ lines.push(`${pad}${key}: {`);
226
+ lines.push(generateConstantObject(value, indent + 2));
227
+ lines.push(`${pad}};`);
228
+ } else {
229
+ lines.push(`${pad}${key}: ${typeof value === 'number' ? value : JSON.stringify(value)};`);
230
+ }
231
+ }
232
+ return lines.join('\n');
233
+ }
234
+
235
+ function generateTypesFile(clusters, clusterDefinitions, constants) {
214
236
  const lines = [];
215
237
 
216
238
  // Header
@@ -309,6 +331,42 @@ type ZCLNodeConstructorInput = {
309
331
  lines.push('}');
310
332
  lines.push('');
311
333
 
334
+ // Generate cluster type lookup maps for inference helpers
335
+ lines.push('/** Cluster type lookup by cluster NAME */');
336
+ lines.push('export interface ClusterTypeByName {');
337
+ for (const cluster of clusters) {
338
+ const interfaceName = toInterfaceName(cluster);
339
+ lines.push(` ${cluster.clusterName}: ${interfaceName};`);
340
+ }
341
+ lines.push('}');
342
+ lines.push('');
343
+
344
+ // Generate cluster attribute lookup maps for inference helpers
345
+ lines.push('/** Cluster attributes lookup by cluster NAME */');
346
+ lines.push('export interface ClusterAttributesByName {');
347
+ for (const cluster of clusters) {
348
+ const interfaceName = toInterfaceName(cluster);
349
+ const attributesInterfaceName = `${interfaceName}Attributes`;
350
+ const attributesType = cluster.attributes.length > 0 ? attributesInterfaceName : 'Record<string, unknown>';
351
+ lines.push(` ${cluster.clusterName}: ${attributesType};`);
352
+ }
353
+ lines.push('}');
354
+ lines.push('');
355
+
356
+ lines.push('/** Infer a typed cluster interface from a CLUSTER definition object. */');
357
+ lines.push('export type ClusterTypeFromDefinition<TDef extends { NAME: string; ID: number }> =');
358
+ lines.push(" TDef['NAME'] extends keyof ClusterTypeByName");
359
+ lines.push(" ? ClusterTypeByName[TDef['NAME']]");
360
+ lines.push(' : ZCLNodeCluster;');
361
+ lines.push('');
362
+
363
+ lines.push('/** Infer typed cluster attribute map from a CLUSTER definition object. */');
364
+ lines.push('export type ClusterAttributesFromDefinition<TDef extends { NAME: string; ID: number }> =');
365
+ lines.push(" TDef['NAME'] extends keyof ClusterAttributesByName");
366
+ lines.push(" ? ClusterAttributesByName[TDef['NAME']]");
367
+ lines.push(' : Record<string, unknown>;');
368
+ lines.push('');
369
+
312
370
  // Generate endpoint type
313
371
  lines.push(`export type ZCLNodeEndpoint = {
314
372
  clusters: ClusterRegistry & {
@@ -327,30 +385,61 @@ export interface ZCLNode {
327
385
  }
328
386
  `);
329
387
 
330
- // Module declaration for CommonJS compatibility
331
- lines.push(`declare module "zigbee-clusters" {
332
- export const ZCLNode: {
333
- new (node: ZCLNodeConstructorInput): ZCLNode;
334
- };
335
- export const CLUSTER: {
336
- [key: string]: { ID: number; NAME: string; ATTRIBUTES: unknown; COMMANDS: unknown };
337
- };
338
- export { ZCLNodeCluster };`);
388
+ // Runtime value exports
389
+ lines.push(`export const ZCLNode: {
390
+ new (node: ZCLNodeConstructorInput): ZCLNode;
391
+ };
339
392
 
340
- // Export all cluster classes
341
- for (const cluster of clusters) {
342
- const interfaceName = toInterfaceName(cluster);
343
- const exportName = cluster.exportName || interfaceName;
344
- lines.push(` export const ${exportName}: {`);
345
- lines.push(` new (...args: any[]): ${interfaceName};`);
393
+ export const CLUSTER: {
394
+ `);
395
+ for (const def of clusterDefinitions) {
396
+ lines.push(` ${def.constantName}: {`);
346
397
  lines.push(' ID: number;');
347
- lines.push(' NAME: string;');
398
+ lines.push(` NAME: '${def.clusterName}';`);
348
399
  lines.push(' ATTRIBUTES: unknown;');
349
400
  lines.push(' COMMANDS: unknown;');
350
401
  lines.push(' };');
351
402
  }
403
+ lines.push('};');
352
404
 
353
- lines.push('}');
405
+ // Export all cluster classes
406
+ for (const cluster of clusters) {
407
+ const interfaceName = toInterfaceName(cluster);
408
+ const exportName = cluster.exportName || interfaceName;
409
+ lines.push(`export const ${exportName}: {`);
410
+ lines.push(` new (...args: any[]): ${interfaceName};`);
411
+ lines.push(` ID: ${cluster.clusterId};`);
412
+ lines.push(` NAME: '${cluster.clusterName}';`);
413
+ lines.push(' ATTRIBUTES: unknown;');
414
+ lines.push(' COMMANDS: unknown;');
415
+ lines.push('};');
416
+ }
417
+
418
+ // Export constants
419
+ if (constants) {
420
+ for (const [name, value] of Object.entries(constants)) {
421
+ lines.push('');
422
+ lines.push(`export const ${name}: {`);
423
+ lines.push(generateConstantObject(value));
424
+ lines.push('};');
425
+ }
426
+ }
427
+
428
+ lines.push('');
429
+ lines.push('declare const _default: {');
430
+ lines.push(' ZCLNode: typeof ZCLNode;');
431
+ lines.push(' CLUSTER: typeof CLUSTER;');
432
+ for (const cluster of clusters) {
433
+ const exportName = cluster.exportName || toInterfaceName(cluster);
434
+ lines.push(` ${exportName}: typeof ${exportName};`);
435
+ }
436
+ if (constants) {
437
+ for (const name of Object.keys(constants)) {
438
+ lines.push(` ${name}: typeof ${name};`);
439
+ }
440
+ }
441
+ lines.push('};');
442
+ lines.push('export default _default;');
354
443
 
355
444
  return lines.join('\n');
356
445
  }
@@ -381,8 +470,20 @@ function main() {
381
470
  // Sort clusters alphabetically
382
471
  clusters.sort((a, b) => a.clusterName.localeCompare(b.clusterName));
383
472
 
473
+ const clusterDefinitions = Object.entries(clustersModule.CLUSTER)
474
+ .map(([constantName, value]) => ({
475
+ constantName,
476
+ clusterId: value.ID,
477
+ clusterName: value.NAME,
478
+ }))
479
+ .sort((a, b) => a.constantName.localeCompare(b.constantName));
480
+
481
+ // Load constants
482
+ const { ZIGBEE_PROFILE_ID, ZIGBEE_DEVICE_ID, IAS_ZONE_TYPE } = require('../lib/constants');
483
+ const constants = { ZIGBEE_PROFILE_ID, ZIGBEE_DEVICE_ID, IAS_ZONE_TYPE };
484
+
384
485
  console.log(`\nGenerating ${OUTPUT_FILE}...`);
385
- const output = generateTypesFile(clusters);
486
+ const output = generateTypesFile(clusters, clusterDefinitions, constants);
386
487
  fs.writeFileSync(OUTPUT_FILE, output);
387
488
 
388
489
  console.log(`Done! Generated types for ${clusters.length} clusters.`);