Skip to content

Commit

Permalink
Throw error when read, write or configureReporting status is not succ…
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenkk committed Mar 31, 2020
1 parent f8191e0 commit d73c42a
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 11 deletions.
17 changes: 15 additions & 2 deletions src/controller/model/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ class Endpoint extends Entity {
* Zigbee functions
*/

private checkStatus(payload: [{status: Zcl.Status}]): void {
for (const item of payload) {
if (item.status !== Zcl.Status.SUCCESS) {
throw new Error(`Status '${Zcl.Status[item.status]}'`);
}
}
}

public async write(
clusterKey: number | string, attributes: KeyValue, options?: Options
): Promise<void> {
Expand All @@ -198,9 +206,10 @@ class Endpoint extends Entity {
Zcl.FrameType.GLOBAL, Zcl.Direction.CLIENT_TO_SERVER, options.disableDefaultResponse,
options.manufacturerCode, ZclTransactionSequenceNumber.next(), 'write', cluster.ID, payload
);
await Entity.adapter.sendZclFrameToEndpoint(
const result = await Entity.adapter.sendZclFrameToEndpoint(
this.deviceNetworkAddress, this.ID, frame, options.timeout,
);
this.checkStatus(result.frame.Payload);
} catch (error) {
const message = `${log} failed (${error})`;
debug.error(message);
Expand Down Expand Up @@ -231,6 +240,7 @@ class Endpoint extends Entity {
const result = await Entity.adapter.sendZclFrameToEndpoint(
this.deviceNetworkAddress, this.ID, frame, options.timeout,
);
this.checkStatus(result.frame.Payload);
return ZclFrameConverter.attributeKeyValue(result.frame);
} catch (error) {
const message = `${log} failed (${error})`;
Expand Down Expand Up @@ -404,7 +414,10 @@ class Endpoint extends Entity {
debug.info(log);

try {
await Entity.adapter.sendZclFrameToEndpoint(this.deviceNetworkAddress, this.ID, frame, options.timeout);
const result = await Entity.adapter.sendZclFrameToEndpoint(
this.deviceNetworkAddress, this.ID, frame, options.timeout
);
this.checkStatus(result.frame.Payload);
} catch (error) {
const message = `${log} failed (${error})`;
debug.error(message);
Expand Down
13 changes: 7 additions & 6 deletions src/zcl/definition/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ enum Status {
SUCCESS = 0,
FAILURE = 1,
NOT_AUTHORIZED = 126,
MALFORMED_CMD = 128,
UNSUP_CLUSTER_CMD = 129,
UNSUP_GENERAL_CMD = 130,
UNSUP_MANU_CLUSTER_CMD = 131,
UNSUP_MANU_GENERAL_CMD = 132,
RESERVED_FIELD_NOT_ZERO = 127,
MALFORMED_COMMAND = 128,
UNSUP_CLUSTER_COMMAND = 129,
UNSUP_GENERAL_COMMAND = 130,
UNSUP_MANUF_CLUSTER_COMMAND = 131,
UNSUP_MANUF_GENERAL_COMMAND = 132,
INVALID_FIELD = 133,
UNSUP_ATTRIBUTE = 134,
UNSUPPORTED_ATTRIBUTE = 134,
INVALID_VALUE = 135,
READ_ONLY = 136,
INSUFFICIENT_SPACE = 137,
Expand Down
4 changes: 3 additions & 1 deletion src/zcl/zclFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,9 @@ class ZclFrame {
}

// List of commands is not completed, feel free to add more.
public isCommand(commandName: 'read' | 'report' | 'readRsp' | 'remove' | 'add' | 'write' | 'enrollReq'): boolean {
public isCommand(
commandName: 'read' | 'report' | 'readRsp' | 'remove' | 'add' | 'write' | 'enrollReq' | 'configReport'
): boolean {
return this.getCommand().name === commandName;
}

Expand Down
48 changes: 46 additions & 2 deletions test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const mocksendZclFrameToEndpoint = jest.fn();

let iasZoneReadState170Count = 0;
let enroll170 = true;
let configureReportStatus = 0;

const restoreMocksendZclFrameToEndpoint = () => {
mocksendZclFrameToEndpoint.mockImplementation((networkAddress, endpoint, frame: ZclFrame) => {
Expand All @@ -67,10 +68,10 @@ const restoreMocksendZclFrameToEndpoint = () => {
for (const item of frame.Payload) {
if (frame.isCluster('ssIasZone') && item.attrId === 0) {
iasZoneReadState170Count++;
payload.push({attrId: item.attrId, attrData: iasZoneReadState170Count === 2 && enroll170 ? 1 : 0});
payload.push({attrId: item.attrId, attrData: iasZoneReadState170Count === 2 && enroll170 ? 1 : 0, status: 0});
} else if (item.attrId !== 65314) {
const attribute = cluster.getAttribute(item.attrId).name;
payload.push({attrId: item.attrId, attrData: mockDevices[networkAddress].attributes[endpoint][attribute]})
payload.push({attrId: item.attrId, attrData: mockDevices[networkAddress].attributes[endpoint][attribute], status: 0})
}
}

Expand Down Expand Up @@ -99,6 +100,26 @@ const restoreMocksendZclFrameToEndpoint = () => {
groupID: 1,
});
}

if (frame.isGlobal() && frame.isCommand('write')) {
const payload = [];
for (const item of frame.Payload) {
payload.push({attrId: item.attrId, status: 0})
}

// @ts-ignore
return {frame: new ZclFrame(null, payload, frame.Cluster)};
}

if (frame.isGlobal() && frame.isCommand('configReport')) {
const payload = [];
for (const item of frame.Payload) {
payload.push({attrId: item.attrId, status: configureReportStatus, direction: 1})
}

// @ts-ignore
return {frame: new ZclFrame(null, payload, frame.Cluster)};
}
})
}

Expand Down Expand Up @@ -368,6 +389,7 @@ describe('Controller', () => {
// @ts-ignore
zclTransactionSequenceNumber.number = 1;
iasZoneReadState170Count = 0;
configureReportStatus = 0;
skipWait = false;
enroll170 = true;
options.network.channelList = [15];
Expand Down Expand Up @@ -1960,6 +1982,28 @@ describe('Controller', () => {
});
});

it('Endpoint configure reporting fails when status code is not 0', async () => {
configureReportStatus = 1;
await controller.start();
await mockAdapterEvents['deviceJoined']({networkAddress: 129, ieeeAddr: '0x129'});
const device = controller.getDeviceByIeeeAddr('0x129');
const endpoint = device.getEndpoint(1);
mocksendZclFrameToEndpoint.mockClear();
let error;
try {
await endpoint.configureReporting('genPowerCfg', [{
attribute: 'mainsFrequency',
minimumReportInterval: 1,
maximumReportInterval: 10,
reportableChange: 1,
}]);
}
catch (e) {
error = e;
}
expect(error).toStrictEqual(new Error(`ConfigureReporting 0x129/1 genPowerCfg([{"attribute":"mainsFrequency","minimumReportInterval":1,"maximumReportInterval":10,"reportableChange":1}], {"timeout":6000,"manufacturerCode":null,"disableDefaultResponse":true}) failed (Error: Status 'FAILURE')`));
});

it('Return group from databse when not in lookup', async () => {
await controller.start();
await controller.createGroup(2);
Expand Down

0 comments on commit d73c42a

Please sign in to comment.