zigbee-clusters 2.5.1 → 2.6.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/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "all",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "plugins": ["prettier-plugin-jsdoc"]
7
+ }
package/README.md CHANGED
@@ -87,9 +87,10 @@ class MyDevice extends Homey.Device {
87
87
  // Zigbee specification and refuses to send a default response.
88
88
  waitForResponse: true,
89
89
 
90
- // This is an optional property that allows for adjusting the response
91
- // timeout (25000ms) before the command is considered rejected.
92
- timeout: 10000,
90
+ // This is an optional property that allows for adjusting the default response
91
+ // timeout (10000ms) before the command is considered rejected.
92
+ // The timeout starts after sending the frame and receiving a low-level ack.
93
+ timeout: 5000,
93
94
  }
94
95
  );
95
96
  });
package/lib/Cluster.js CHANGED
@@ -340,16 +340,18 @@ class Cluster extends EventEmitter {
340
340
  * discover any manufacture- specific
341
341
  * commands.
342
342
  *
343
- * @param {object} [opts=]
344
- * @param {number} [opts.startValue=0]
345
- * @param {number} [opts.maxResults=250]
343
+ * @param {object} [params=]
344
+ * @param {number} [params.startValue=0]
345
+ * @param {number} [params.maxResults=250]
346
+ * @param {object} [opts=] - Optional parameters
347
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
346
348
  * @returns {Promise<number[]>}
347
349
  */
348
- async discoverCommandsGenerated({ startValue = 0, maxResults = 250 } = {}) {
350
+ async discoverCommandsGenerated({ startValue = 0, maxResults = 250 } = {}, opts) {
349
351
  const { commandIds } = await super.discoverCommandsGenerated({
350
352
  startValue,
351
353
  maxResults,
352
- });
354
+ }, opts);
353
355
 
354
356
  const res = commandIds.map(cId => ((this.constructor.commandsById[cId] || [])
355
357
  .filter(c => !c.global)
@@ -375,16 +377,18 @@ class Cluster extends EventEmitter {
375
377
  * a manufacturer-specific cluster. A manufacturer ID in this field of 0xffff (wildcard) will
376
378
  * discover any manufacture- specific commands.
377
379
  *
378
- * @param {object} [opts=]
379
- * @param {number} [opts.startValue=0]
380
- * @param {number} [opts.maxResults=255]
380
+ * @param {object} [params=]
381
+ * @param {number} [params.startValue=0]
382
+ * @param {number} [params.maxResults=255]
383
+ * @param {object} [opts=] - Optional parameters
384
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
381
385
  * @returns {Promise<number[]>}
382
386
  */
383
- async discoverCommandsReceived({ startValue = 0, maxResults = 255 } = {}) {
387
+ async discoverCommandsReceived({ startValue = 0, maxResults = 255 } = {}, opts) {
384
388
  const { commandIds } = await super.discoverCommandsReceived({
385
389
  startValue,
386
390
  maxResults,
387
- });
391
+ }, opts);
388
392
 
389
393
  const res = commandIds.map(cId => ((this.constructor.commandsById[cId] || [])
390
394
  .filter(c => !c.global)
@@ -400,7 +404,8 @@ class Cluster extends EventEmitter {
400
404
  * Command which reads a given set of attributes from the remote cluster.
401
405
  * Note: do not mix regular and manufacturer specific attributes.
402
406
  * @param {string[]} attributeNames
403
- * @param {{timeout: number}} [opts=]
407
+ * @param {object} [opts=] - Optional parameters
408
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
404
409
  * @returns {Promise<Object.<string, unknown>>} - Object with values (e.g. `{ onOff: true }`)
405
410
  */
406
411
  async readAttributes(attributeNames, opts) {
@@ -453,9 +458,11 @@ class Cluster extends EventEmitter {
453
458
  * Note: do not mix regular and manufacturer specific attributes.
454
459
  * @param {object} attributes - Object with attribute names as keys and their values (e.g. `{
455
460
  * onOff: true, fakeAttributeName: 10 }`.
461
+ * @param {object} [opts=] - Optional parameters
462
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
456
463
  * @returns {Promise<*|{attributes: *}>}
457
464
  */
458
- async writeAttributes(attributes = {}) {
465
+ async writeAttributes(attributes = {}, opts) {
459
466
  const arr = Object.keys(attributes).map(n => {
460
467
  const attr = this.constructor.attributes[n];
461
468
  if (!attr) {
@@ -477,7 +484,7 @@ class Cluster extends EventEmitter {
477
484
 
478
485
  debug(this.logId, 'write attributes', attributes, manufacturerId ? `manufacturer specific id ${manufacturerId}` : '');
479
486
 
480
- return super.writeAttributes({ attributes: data, manufacturerId });
487
+ return super.writeAttributes({ attributes: data, manufacturerId }, opts);
481
488
  }
482
489
 
483
490
  /**
@@ -485,9 +492,11 @@ class Cluster extends EventEmitter {
485
492
  * Note: do not mix regular and manufacturer specific attributes.
486
493
  * @param {object} attributes - Attribute reporting configuration (e.g. `{ onOff: {
487
494
  * minInterval: 0, maxInterval: 300, minChange: 1 } }`)
495
+ * @param {object} [opts=] - Optional parameters
496
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
488
497
  * @returns {Promise<void>}
489
498
  */
490
- async configureReporting(attributes = {}) {
499
+ async configureReporting(attributes = {}, opts) {
491
500
  const req = [];
492
501
  // eslint-disable-next-line guard-for-in,no-restricted-syntax
493
502
  for (const attributeName in attributes) {
@@ -524,7 +533,7 @@ class Cluster extends EventEmitter {
524
533
 
525
534
  debug(this.logId, 'configure reporting', req, manufacturerId ? `manufacturer specific id ${manufacturerId}` : '');
526
535
 
527
- const { reports } = await super.configureReporting({ reports: req, manufacturerId });
536
+ const { reports } = await super.configureReporting({ reports: req, manufacturerId }, opts);
528
537
 
529
538
  debug(this.logId, 'configured reporting', reports);
530
539
  for (const result of reports) {
@@ -554,10 +563,12 @@ class Cluster extends EventEmitter {
554
563
  * interesting).
555
564
  * Note: do not mix regular and manufacturer specific attributes.
556
565
  * @param {Array} attributes - Array with number/strings (either attribute id, or attribute name).
566
+ * @param {object} [opts=] - Optional parameters
567
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
557
568
  * @returns {Promise<ReadReportingConfiguration[]>} - Returns array with
558
569
  * ReadReportingConfiguration objects per attribute.
559
570
  */
560
- async readReportingConfiguration(attributes = []) {
571
+ async readReportingConfiguration(attributes = [], opts) {
561
572
  const req = [];
562
573
 
563
574
  // Loop all the provided attributes
@@ -601,7 +612,7 @@ class Cluster extends EventEmitter {
601
612
  const { reports } = await super.readReportingConfiguration({
602
613
  attributes: req,
603
614
  manufacturerId,
604
- });
615
+ }, opts);
605
616
  debug(this.logId, 'read reporting configuration result', reports);
606
617
 
607
618
  // Return the parsed reports
@@ -624,15 +635,17 @@ class Cluster extends EventEmitter {
624
635
  * in a ZigBee cluster or 1 to discover manufacturer specific attributes in either a standard
625
636
  * or a manufacturer specific cluster.
626
637
  *
638
+ * @param {object} [opts=] - Optional parameters
639
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
627
640
  * @returns {Promise<Array>} - Array with string or number values (depending on if the
628
641
  * attribute
629
642
  * is implemented in zigbee-clusters or not).
630
643
  */
631
- async discoverAttributes() {
644
+ async discoverAttributes(opts) {
632
645
  const { attributes } = await super.discoverAttributes({
633
646
  startValue: 0,
634
647
  maxResults: 255,
635
- });
648
+ }, opts);
636
649
 
637
650
  const result = [];
638
651
  for (const attr of attributes) {
@@ -660,16 +673,18 @@ class Cluster extends EventEmitter {
660
673
  * or a manufacturer- specific cluster. A manufacturer ID in this field of 0xffff (wildcard)
661
674
  * will discover any manufacture-specific attributes.
662
675
  *
676
+ * @param {object} [opts=] - Optional parameters
677
+ * @param {number} [opts.timeout] - Optional timeout in milliseconds for waiting for response
663
678
  * @returns {Promise<Array>} - Returns an array with objects with attribute names as keys and
664
679
  * following object as values: `{name: string, id: number, acl: { readable: boolean, writable:
665
680
  * boolean, reportable: boolean } }`. Note that `name` is optional based on whether the
666
681
  * attribute is implemented in zigbee-clusters.
667
682
  */
668
- async discoverAttributesExtended() {
683
+ async discoverAttributesExtended(opts) {
669
684
  const { attributes } = await super.discoverAttributesExtended({
670
685
  startValue: 0,
671
686
  maxResults: 250,
672
- });
687
+ }, opts);
673
688
 
674
689
  const result = [];
675
690
  for (const attr of attributes) {
@@ -956,23 +971,6 @@ class Cluster extends EventEmitter {
956
971
  return this._nextTrxSeqNr;
957
972
  }
958
973
 
959
- async _awaitPacket(trxSequenceNumber, timeout = 25000) {
960
- if (this._trxHandlers[trxSequenceNumber]) {
961
- throw new Error(`already waiting for this trx: ${trxSequenceNumber}`);
962
- }
963
- return new Promise((resolve, reject) => {
964
- const t = setTimeout(() => {
965
- delete this._trxHandlers[trxSequenceNumber];
966
- reject(new Error('Timeout: Expected Response'));
967
- }, timeout);
968
- this._trxHandlers[trxSequenceNumber] = async frame => {
969
- delete this._trxHandlers[trxSequenceNumber];
970
- resolve(frame);
971
- clearTimeout(t);
972
- };
973
- });
974
- }
975
-
976
974
  // / START STATIC METHODS
977
975
 
978
976
  // Adds command proxy stubs to a proto object which is one level higher.
@@ -1106,15 +1104,54 @@ class Cluster extends EventEmitter {
1106
1104
  return this.sendFrame(payload);
1107
1105
  }
1108
1106
 
1109
- // Check if a valid timeout override is provided
1110
- let responseTimeout;
1107
+ // Timeout between getting a low-level ack and receiving the ZCL response
1108
+ let responseTimeout = 10_000;
1109
+
1110
+ // Check if a timeout override is provided
1111
1111
  if (typeof opts.timeout === 'number') {
1112
1112
  responseTimeout = opts.timeout;
1113
1113
  }
1114
1114
 
1115
+ if (this._trxHandlers[payload.trxSequenceNumber]) {
1116
+ throw new Error(`already waiting for this trx: ${payload.trxSequenceNumber}`);
1117
+ }
1118
+
1119
+ let rejectAwaitResponse;
1120
+ let resolveAwaitResponse;
1121
+ let awaitResponseTimeout;
1115
1122
  const [response] = await Promise.all([
1116
- this._awaitPacket(payload.trxSequenceNumber, responseTimeout),
1117
- this.sendFrame(payload),
1123
+ new Promise((resolve, reject) => {
1124
+ rejectAwaitResponse = reject;
1125
+ resolveAwaitResponse = resolve;
1126
+
1127
+ this._trxHandlers[payload.trxSequenceNumber] = async frame => {
1128
+ delete this._trxHandlers[payload.trxSequenceNumber];
1129
+ if (awaitResponseTimeout) clearTimeout(awaitResponseTimeout);
1130
+ resolveAwaitResponse(frame);
1131
+ };
1132
+ }),
1133
+
1134
+ // Send the frame and when the frame is actually sent start the timeout for receiving
1135
+ // the response, sending the frame could be queued or delayed for other reasons
1136
+ // so only start the timeout when it is certain that the frame is sent.
1137
+ this.sendFrame(payload)
1138
+ .then(() => {
1139
+ if (typeof responseTimeout !== 'number') return;
1140
+
1141
+ // Make sure the response is still expected
1142
+ if (this._trxHandlers[payload.trxSequenceNumber] == null) return;
1143
+
1144
+ awaitResponseTimeout = setTimeout(() => {
1145
+ delete this._trxHandlers[payload.trxSequenceNumber];
1146
+ rejectAwaitResponse(new Error('Timeout: Expected Response'));
1147
+ }, responseTimeout);
1148
+ })
1149
+ .catch(err => {
1150
+ delete this._trxHandlers[payload.trxSequenceNumber];
1151
+ if (awaitResponseTimeout) clearTimeout(awaitResponseTimeout);
1152
+ rejectAwaitResponse(err);
1153
+ throw err;
1154
+ }),
1118
1155
  ]);
1119
1156
 
1120
1157
  if (response instanceof this.constructor.defaultResponseArgsType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-clusters",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Zigbee Cluster Library for Node.js",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",