Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(cluster): should respond when disable default response is true #142

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 69 additions & 48 deletions lib/Endpoint.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,43 +104,51 @@ class Endpoint extends EventEmitter {
*/
async handleFrame(clusterId, frame, meta) {
const rawFrame = frame;
frame = Endpoint.parseFrame(frame);

if (rawFrame[0] & 0x4) {
frame = ZCLMfgSpecificHeader.fromBuffer(rawFrame);
} else frame = ZCLStandardHeader.fromBuffer(rawFrame);

// NOTE: we do not respond with a default response if:
// 1. The frame we received is a default response (frame.cmdId = 11)
// 2. Another command is sent in response to the received frame
// 3. The frame has the disableDefaultResponse flag set
// See ZCL specification 2.5.12.2.
const response = (
frame.frameControl.disableDefaultResponse
|| (meta && meta.groupId)
|| frame.cmdId === 11
) ? null : this._makeErrorResponse(frame);

let clusterSpecificResponse = null;
let clusterSpecificError = null;
try {
const result = await this.handleZCLFrame(clusterId, frame, meta, rawFrame);
if (!response) return;
if (result) {
const [cmdId, data] = result;
response.data = data.toBuffer();
response.cmdId = cmdId;
} else {
// Set status to success
response.data[1] = 0;
}
} catch (e) {
debug(`${this.getLogId(clusterId)}, error while handling frame`, e.message, { meta, frame });
clusterSpecificResponse = await this.handleZCLFrame(clusterId, frame, meta, rawFrame);
} catch (err) {
clusterSpecificError = err;
debug(`${this.getLogId(clusterId)}, error while handling frame`, err.message, { meta, frame });
}
// If desired (disableDefaultResponse: false) try to respond to the incoming frame
if (response) {
try {
await this.sendFrame(clusterId, response.toBuffer());
} catch (err) {
debug(`${this.getLogId(clusterId)}, error while responding with \`send frame\` to \`handle frame\``, err, { response });
}

// Don't respond to this frame if it is a default response or a group cast (ZCL spec 2.5.12.2)
if (frame.cmdId === 11 || (meta && typeof meta.groupId === 'number')) return;

// If cluster specific error, respond with a default response error frame
if (clusterSpecificError) {
const defaultResponseErrorFrame = this.makeDefaultResponseFrame(frame, false);
this.sendFrame(clusterId, defaultResponseErrorFrame.toBuffer()).catch(err => {
debug(`${this.getLogId(clusterId)}, error while sending default error response`, err, { response: defaultResponseErrorFrame });
});

// No further handling for this frame
return;
}

// Create response frame and set status to success
const responseFrame = this.makeDefaultResponseFrame(frame, true);

// If a cluster specific response was generated, set the response data
// and cmdId in the response frame.
if (clusterSpecificResponse) {
const [cmdId, data] = clusterSpecificResponse;
responseFrame.data = data.toBuffer();
responseFrame.cmdId = cmdId;
}

// If there was no cluster specific response and the default response is disabled, don't
// send a response.
if (!clusterSpecificResponse && frame.frameControl.disableDefaultResponse) return;

// Send either cluster specific, or default response frame
try {
await this.sendFrame(clusterId, responseFrame.toBuffer());
} catch (err) {
debug(`${this.getLogId(clusterId)}, error while sending cluster specific or default success response`, err, { response: responseFrame });
}
}

Expand Down Expand Up @@ -173,25 +181,38 @@ class Endpoint extends EventEmitter {
return response;
}

_makeErrorResponse(frame) {
let result;
if (frame instanceof ZCLStandardHeader) {
result = new ZCLStandardHeader();
/**
* Returns a default response frame with an error status code.
* @param {*} receivedFrame
* @param {boolean} success
* @returns {ZCLStandardHeader|ZCLMfgSpecificHeader}
*/
makeDefaultResponseFrame(receivedFrame, success) {
let responseFrame;
if (receivedFrame instanceof ZCLStandardHeader) {
responseFrame = new ZCLStandardHeader();
} else {
result = new ZCLMfgSpecificHeader();
result.manufacturerId = frame.manufacturerId;
responseFrame = new ZCLMfgSpecificHeader();
responseFrame.manufacturerId = receivedFrame.manufacturerId;
}
// TODO: flip proper bits
result.frameControl = frame.frameControl.copy();
responseFrame.frameControl = receivedFrame.frameControl.copy();

responseFrame.frameControl.disableDefaultResponse = true;
responseFrame.frameControl.clusterSpecific = false;
responseFrame.frameControl.directionToClient = !receivedFrame.frameControl.directionToClient;

result.frameControl.disableDefaultResponse = true;
result.frameControl.clusterSpecific = false;
result.frameControl.directionToClient = !frame.frameControl.directionToClient;
responseFrame.trxSequenceNumber = receivedFrame.trxSequenceNumber;
responseFrame.cmdId = 0x0B;
responseFrame.data = Buffer.from([receivedFrame.cmdId, success ? 0 : 1]);
return responseFrame;
}

result.trxSequenceNumber = frame.trxSequenceNumber;
result.cmdId = 0x0B;
result.data = Buffer.from([frame.cmdId, 0x01]);
return result;
static parseFrame(frame) {
if (frame[0] & 0x4) {
return ZCLMfgSpecificHeader.fromBuffer(frame);
}
return ZCLStandardHeader.fromBuffer(frame);
}

}
Expand Down
Loading
Loading