zigbee-clusters 2.4.1 → 2.5.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.d.ts CHANGED
@@ -173,6 +173,12 @@ interface ZCLNodeCluster extends EventEmitter {
173
173
  discoverAttributesExtended(): Promise<any[]>;
174
174
  }
175
175
 
176
+ interface BasicCluster extends ZCLNodeCluster {
177
+ factoryReset(): Promise<void>;
178
+ }
179
+
180
+ interface PowerConfigurationCluster extends ZCLNodeCluster {}
181
+
176
182
  interface OnOffCluster extends ZCLNodeCluster {
177
183
  setOn(): Promise<void>;
178
184
  setOff(): Promise<void>;
@@ -309,7 +315,9 @@ declare module "zigbee-clusters" {
309
315
  [key: string]: { ID: number; NAME: string; ATTRIBUTES: any; COMMANDS: any };
310
316
  };
311
317
  export var ZCLNodeCluster;
318
+ export var BasicCluster;
312
319
  export var OnOffCluster;
313
320
  export var LevelControlCluster;
314
321
  export var ColorControlCluster;
322
+ export var PowerConfigurationCluster;
315
323
  }
package/lib/Endpoint.js CHANGED
@@ -104,43 +104,51 @@ class Endpoint extends EventEmitter {
104
104
  */
105
105
  async handleFrame(clusterId, frame, meta) {
106
106
  const rawFrame = frame;
107
+ frame = Endpoint.parseFrame(frame);
107
108
 
108
- if (rawFrame[0] & 0x4) {
109
- frame = ZCLMfgSpecificHeader.fromBuffer(rawFrame);
110
- } else frame = ZCLStandardHeader.fromBuffer(rawFrame);
111
-
112
- // NOTE: we do not respond with a default response if:
113
- // 1. The frame we received is a default response (frame.cmdId = 11)
114
- // 2. Another command is sent in response to the received frame
115
- // 3. The frame has the disableDefaultResponse flag set
116
- // See ZCL specification 2.5.12.2.
117
- const response = (
118
- frame.frameControl.disableDefaultResponse
119
- || (meta && meta.groupId)
120
- || frame.cmdId === 11
121
- ) ? null : this._makeErrorResponse(frame);
122
-
109
+ let clusterSpecificResponse = null;
110
+ let clusterSpecificError = null;
123
111
  try {
124
- const result = await this.handleZCLFrame(clusterId, frame, meta, rawFrame);
125
- if (!response) return;
126
- if (result) {
127
- const [cmdId, data] = result;
128
- response.data = data.toBuffer();
129
- response.cmdId = cmdId;
130
- } else {
131
- // Set status to success
132
- response.data[1] = 0;
133
- }
134
- } catch (e) {
135
- debug(`${this.getLogId(clusterId)}, error while handling frame`, e.message, { meta, frame });
112
+ clusterSpecificResponse = await this.handleZCLFrame(clusterId, frame, meta, rawFrame);
113
+ } catch (err) {
114
+ clusterSpecificError = err;
115
+ debug(`${this.getLogId(clusterId)}, error while handling frame`, err.message, { meta, frame });
136
116
  }
137
- // If desired (disableDefaultResponse: false) try to respond to the incoming frame
138
- if (response) {
139
- try {
140
- await this.sendFrame(clusterId, response.toBuffer());
141
- } catch (err) {
142
- debug(`${this.getLogId(clusterId)}, error while responding with \`send frame\` to \`handle frame\``, err, { response });
143
- }
117
+
118
+ // Don't respond to this frame if it is a default response or a group cast (ZCL spec 2.5.12.2)
119
+ if (frame.cmdId === 11 || (meta && typeof meta.groupId === 'number')) return;
120
+
121
+ // If cluster specific error, respond with a default response error frame
122
+ if (clusterSpecificError) {
123
+ const defaultResponseErrorFrame = this.makeDefaultResponseFrame(frame, false);
124
+ this.sendFrame(clusterId, defaultResponseErrorFrame.toBuffer()).catch(err => {
125
+ debug(`${this.getLogId(clusterId)}, error while sending default error response`, err, { response: defaultResponseErrorFrame });
126
+ });
127
+
128
+ // No further handling for this frame
129
+ return;
130
+ }
131
+
132
+ // Create response frame and set status to success
133
+ const responseFrame = this.makeDefaultResponseFrame(frame, true);
134
+
135
+ // If a cluster specific response was generated, set the response data
136
+ // and cmdId in the response frame.
137
+ if (clusterSpecificResponse) {
138
+ const [cmdId, data] = clusterSpecificResponse;
139
+ responseFrame.data = data.toBuffer();
140
+ responseFrame.cmdId = cmdId;
141
+ }
142
+
143
+ // If there was no cluster specific response and the default response is disabled, don't
144
+ // send a response.
145
+ if (!clusterSpecificResponse && frame.frameControl.disableDefaultResponse) return;
146
+
147
+ // Send either cluster specific, or default response frame
148
+ try {
149
+ await this.sendFrame(clusterId, responseFrame.toBuffer());
150
+ } catch (err) {
151
+ debug(`${this.getLogId(clusterId)}, error while sending cluster specific or default success response`, err, { response: responseFrame });
144
152
  }
145
153
  }
146
154
 
@@ -173,25 +181,38 @@ class Endpoint extends EventEmitter {
173
181
  return response;
174
182
  }
175
183
 
176
- _makeErrorResponse(frame) {
177
- let result;
178
- if (frame instanceof ZCLStandardHeader) {
179
- result = new ZCLStandardHeader();
184
+ /**
185
+ * Returns a default response frame with an error status code.
186
+ * @param {*} receivedFrame
187
+ * @param {boolean} success
188
+ * @returns {ZCLStandardHeader|ZCLMfgSpecificHeader}
189
+ */
190
+ makeDefaultResponseFrame(receivedFrame, success) {
191
+ let responseFrame;
192
+ if (receivedFrame instanceof ZCLStandardHeader) {
193
+ responseFrame = new ZCLStandardHeader();
180
194
  } else {
181
- result = new ZCLMfgSpecificHeader();
182
- result.manufacturerId = frame.manufacturerId;
195
+ responseFrame = new ZCLMfgSpecificHeader();
196
+ responseFrame.manufacturerId = receivedFrame.manufacturerId;
183
197
  }
184
198
  // TODO: flip proper bits
185
- result.frameControl = frame.frameControl.copy();
199
+ responseFrame.frameControl = receivedFrame.frameControl.copy();
200
+
201
+ responseFrame.frameControl.disableDefaultResponse = true;
202
+ responseFrame.frameControl.clusterSpecific = false;
203
+ responseFrame.frameControl.directionToClient = !receivedFrame.frameControl.directionToClient;
186
204
 
187
- result.frameControl.disableDefaultResponse = true;
188
- result.frameControl.clusterSpecific = false;
189
- result.frameControl.directionToClient = !frame.frameControl.directionToClient;
205
+ responseFrame.trxSequenceNumber = receivedFrame.trxSequenceNumber;
206
+ responseFrame.cmdId = 0x0B;
207
+ responseFrame.data = Buffer.from([receivedFrame.cmdId, success ? 0 : 1]);
208
+ return responseFrame;
209
+ }
190
210
 
191
- result.trxSequenceNumber = frame.trxSequenceNumber;
192
- result.cmdId = 0x0B;
193
- result.data = Buffer.from([frame.cmdId, 0x01]);
194
- return result;
211
+ static parseFrame(frame) {
212
+ if (frame[0] & 0x4) {
213
+ return ZCLMfgSpecificHeader.fromBuffer(frame);
214
+ }
215
+ return ZCLStandardHeader.fromBuffer(frame);
195
216
  }
196
217
 
197
218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigbee-clusters",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "description": "Zigbee Cluster Library for Node.js",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -32,6 +32,7 @@
32
32
  "homepage": "https://github.com/athombv/node-zigbee-clusters#readme",
33
33
  "devDependencies": {
34
34
  "@athombv/jsdoc-template": "^1.6.1",
35
+ "@types/sinon": "^17.0.3",
35
36
  "concurrently": "^5.2.0",
36
37
  "eslint": "^6.8.0",
37
38
  "eslint-config-athom": "^2.1.0",
@@ -39,6 +40,7 @@
39
40
  "jsdoc-ts-utils": "^2.0.0",
40
41
  "mocha": "^10.1.0",
41
42
  "serve": "^14.0.1",
43
+ "sinon": "^19.0.2",
42
44
  "watch": "^1.0.2"
43
45
  },
44
46
  "dependencies": {