zigbee-herdsman
Version:
An open source ZigBee gateway solution with node.js.
2,833 lines • 108 kB
text/typescript
import * as Zcl from "../src/zspec/zcl";
import {BuffaloZcl} from "../src/zspec/zcl/buffaloZcl";
import {BuffaloZclDataType, DataType, Direction, FrameType, StructuredIndicatorType} from "../src/zspec/zcl/definition/enums";
describe("Zcl", () => {
it("Get cluster by name", () => {
const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {});
expect(cluster.ID).toBe(3);
expect(cluster.name).toBe("genIdentify");
expect(cluster.commands.identifyQuery.ID).toBe(1);
expect(cluster.commands.identifyQuery.name).toBe("identifyQuery");
expect(cluster.commandsResponse.identifyQueryRsp.ID).toBe(0);
expect(cluster.commandsResponse.identifyQueryRsp.name).toBe("identifyQueryRsp");
});
it("Get cluster by ID", () => {
const cluster1 = Zcl.Utils.getCluster(0, undefined, {});
// @ts-expect-error testing
delete cluster1.getAttribute;
// @ts-expect-error testing
delete cluster1.getCommand;
// @ts-expect-error testing
delete cluster1.hasAttribute;
// @ts-expect-error testing
delete cluster1.getCommandResponse;
const cluster2 = Zcl.Utils.getCluster("genBasic", undefined, {});
// @ts-expect-error testing
delete cluster2.getAttribute;
// @ts-expect-error testing
delete cluster2.getCommand;
// @ts-expect-error testing
delete cluster2.hasAttribute;
// @ts-expect-error testing
delete cluster2.getCommandResponse;
expect(cluster1).toStrictEqual(cluster2);
});
it("Get cluster attribute by ID", () => {
const cluster = Zcl.Utils.getCluster(0, undefined, {});
const attribute = cluster.getAttribute(1);
expect(attribute).toStrictEqual({ID: 1, type: DataType.UINT8, name: "appVersion"});
});
it("Cluster has attribute", () => {
const cluster = Zcl.Utils.getCluster(0, undefined, {});
expect(cluster.hasAttribute("zclVersion")).toBeTruthy();
expect(cluster.hasAttribute("NOTEXISTING")).toBeFalsy();
expect(cluster.hasAttribute(0)).toBeTruthy();
expect(cluster.hasAttribute(910293)).toBeFalsy();
});
it("Get specific command by name", () => {
const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {});
const command = cluster.getCommand("ezmodeInvoke");
expect(command.ID).toBe(2);
expect(command.name).toBe("ezmodeInvoke");
});
it("Get global command by name", () => {
const command = Zcl.Utils.getGlobalCommand("readRsp");
expect(command.ID).toBe(1);
expect(command.name).toBe("readRsp");
});
it("Get global command non existing", () => {
expect(() => {
Zcl.Utils.getGlobalCommand("nonexisting");
}).toThrow("Global command with key 'nonexisting' does not exist");
});
it("Get cluster by name non-existing", () => {
expect(() => {
Zcl.Utils.getCluster("notExisting", undefined, {});
}).toThrow("Cluster with name 'notExisting' does not exist");
});
it("Get cluster by id non-existing", () => {
expect(JSON.parse(JSON.stringify(Zcl.Utils.getCluster(0x190231, undefined, {})))).toStrictEqual({
ID: 1638961,
attributes: {},
name: "1638961",
commands: {},
commandsResponse: {},
});
});
it("Get specific command by ID", () => {
const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {});
const command = cluster.getCommand(2);
expect(command).toStrictEqual(cluster.getCommand("ezmodeInvoke"));
});
it("Get specific command by name server to client", () => {
const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {});
const command = cluster.getCommandResponse(0);
expect(command.ID).toBe(0);
expect(command.name).toBe("identifyQueryRsp");
});
it("Get specific command by name non existing", () => {
expect(() => {
const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {});
cluster.getCommandResponse("nonexisting");
}).toThrow("Cluster 'genIdentify' has no command response 'nonexisting'");
});
it("Get discrete or analog of unkown type", () => {
expect(() => {
// @ts-expect-error invalid on purpose
Zcl.Utils.getDataTypeClass(99999);
}).toThrow("Don't know value type for 'undefined'");
});
it("ZclFrame from buffer parse payload with unknown frame type", () => {
expect(() => {
// @ts-ignore
Zcl.Frame.parsePayload({frameControl: {frameType: 9}}, undefined);
}).toThrow("Unsupported frameType '9'");
});
it("ZclFrame from buffer report", () => {
const buffer = Buffer.from([0x18, 0x4a, 0x0a, 0x55, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genAnalogInput.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
74,
10,
);
const payload = [
{
attrData: 0,
attrId: 85,
dataType: 57,
},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
expect(frame.header.isGlobal).toBe(true);
expect(frame.header.isSpecific).toBe(false);
expect(frame.isCluster("genAnalogInput")).toBe(true);
expect(frame.isCommand("report")).toBe(true);
});
it("ZclFrame from buffer tradfriArrowSingle", () => {
const buffer = Buffer.from([0x05, 0x7c, 0x11, 0x1d, 0x07, 0x00, 0x01, 0x0d, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genScenes.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: false,
frameType: 1,
manufacturerSpecific: true,
},
4476,
29,
7,
);
const payload = {value: 256, value2: 13};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
expect(frame.command.ID).toStrictEqual(7);
expect(frame.command.name).toStrictEqual("tradfriArrowSingle");
});
it("ZclFrame from buffer genGroups getMembership", () => {
const buffer = Buffer.from([0x11, 0x7c, 0x02, 2, 10, 0, 20, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genGroups.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
124,
2,
);
const payload = {groupcount: 2, grouplist: [10, 20]};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer genGroups getMembership", () => {
const buffer = Buffer.from([0x19, 0x7c, 0x03, 0, 10, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genGroups.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
124,
3,
);
const payload = {groupid: 10, status: 0};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer occupancy report", () => {
const buffer = Buffer.from([24, 169, 10, 0, 0, 24, 1]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.msOccupancySensing.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
169,
10,
);
const payload = [{attrId: 0, dataType: 24, attrData: 1}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp - short", () => {
const buffer = Buffer.from([0x08, 0x01, 0x07, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genPowerCfg.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
7,
);
const payload = [{status: 0}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp - long", () => {
const buffer = Buffer.from([0x08, 0x01, 0x07, 0x00, 0x01, 0x34, 0x12, 0x01, 0x01, 0x35, 0x12]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genPowerCfg.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
7,
);
const payload = [
{status: 0, direction: 1, attrId: 0x1234},
{status: 1, direction: 1, attrId: 0x1235},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp (hvacThermostat)", () => {
const buffer = Buffer.from([0x18, 0x03, 0x07, 0x00, 0x00, 0x12, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.hvacThermostat.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
3,
7,
);
const payload = [{status: 0, direction: 0, attrId: 18}];
expect(frame.payload).toStrictEqual(payload);
expect(frame.header).toStrictEqual(header);
});
it("ZclFrame from buffer getWeeklyScheduleRsp (hvacThermostat)", () => {
const bufferHeat = Buffer.from([
9, 7, 0, 6, 64, 1, 104, 1, 252, 8, 58, 2, 152, 8, 208, 2, 102, 8, 72, 3, 102, 8, 222, 3, 252, 8, 100, 5, 52, 8,
]);
const frameHeat = Zcl.Frame.fromBuffer(Zcl.Clusters.hvacThermostat.ID, Zcl.Header.fromBuffer(bufferHeat)!, bufferHeat, {});
expect(frameHeat.payload).toStrictEqual({
numoftrans: 6,
dayofweek: 64,
mode: 1,
transitions: [
{transitionTime: 360, heatSetpoint: 2300},
{transitionTime: 570, heatSetpoint: 2200},
{transitionTime: 720, heatSetpoint: 2150},
{transitionTime: 840, heatSetpoint: 2150},
{transitionTime: 990, heatSetpoint: 2300},
{transitionTime: 1380, heatSetpoint: 2100},
],
});
const bufferCool = Buffer.from([
9, 7, 0, 6, 64, 2, 104, 1, 252, 8, 58, 2, 152, 8, 208, 2, 102, 8, 72, 3, 102, 8, 222, 3, 252, 8, 100, 5, 52, 8,
]);
const frameCool = Zcl.Frame.fromBuffer(Zcl.Clusters.hvacThermostat.ID, Zcl.Header.fromBuffer(bufferCool)!, bufferCool, {});
expect(frameCool.payload).toStrictEqual({
numoftrans: 6,
dayofweek: 64,
mode: 2,
transitions: [
{transitionTime: 360, coolSetpoint: 2300},
{transitionTime: 570, coolSetpoint: 2200},
{transitionTime: 720, coolSetpoint: 2150},
{transitionTime: 840, coolSetpoint: 2150},
{transitionTime: 990, coolSetpoint: 2300},
{transitionTime: 1380, coolSetpoint: 2100},
],
});
const bufferHeatAndCool = Buffer.from([9, 7, 0, 1, 64, 3, 104, 1, 252, 8, 58, 2]);
const frameHeatAndCool = Zcl.Frame.fromBuffer(
Zcl.Clusters.hvacThermostat.ID,
Zcl.Header.fromBuffer(bufferHeatAndCool)!,
bufferHeatAndCool,
{},
);
expect(frameHeatAndCool.payload).toStrictEqual({
numoftrans: 1,
dayofweek: 64,
mode: 3,
transitions: [{transitionTime: 360, coolSetpoint: 570, heatSetpoint: 2300}],
});
});
it("ZclFrame to buffer setWeeklyScheduleRsp (hvacThermostat)", () => {
const payloadHeat = {
numoftrans: 6,
dayofweek: 64,
mode: 1,
transitions: [
{transitionTime: 360, heatSetpoint: 23},
{transitionTime: 570, heatSetpoint: 2200},
{transitionTime: 720, heatSetpoint: 2150},
{transitionTime: 840, heatSetpoint: 2150},
{transitionTime: 990, heatSetpoint: 2300},
{transitionTime: 1380, heatSetpoint: 2100},
],
};
const frameHeat = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.CLIENT_TO_SERVER,
false,
undefined,
8,
"setWeeklySchedule",
513,
payloadHeat,
{},
);
expect(frameHeat.toBuffer()).toStrictEqual(
Buffer.from([1, 8, 1, 6, 64, 1, 104, 1, 23, 0, 58, 2, 152, 8, 208, 2, 102, 8, 72, 3, 102, 8, 222, 3, 252, 8, 100, 5, 52, 8]),
);
const payloadCool = {
numoftrans: 6,
dayofweek: 64,
mode: 2,
transitions: [
{transitionTime: 360, coolSetpoint: 2300},
{transitionTime: 570, coolSetpoint: 2200},
{transitionTime: 720, coolSetpoint: 2150},
{transitionTime: 840, coolSetpoint: 2150},
{transitionTime: 990, coolSetpoint: 2300},
{transitionTime: 1380, coolSetpoint: 2100},
],
};
const frameCool = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.CLIENT_TO_SERVER,
false,
undefined,
8,
"setWeeklySchedule",
513,
payloadCool,
{},
);
expect(frameCool.toBuffer()).toStrictEqual(
Buffer.from([1, 8, 1, 6, 64, 2, 104, 1, 252, 8, 58, 2, 152, 8, 208, 2, 102, 8, 72, 3, 102, 8, 222, 3, 252, 8, 100, 5, 52, 8]),
);
const payloadHeatAndCool = {
numoftrans: 6,
dayofweek: 64,
mode: 2,
transitions: [{transitionTime: 360, coolSetpoint: 570, heatSetpoint: 2300}],
};
const frameHeatAndCool = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.CLIENT_TO_SERVER,
false,
undefined,
8,
"setWeeklySchedule",
513,
payloadHeatAndCool,
{},
);
expect(frameHeatAndCool.toBuffer()).toStrictEqual(Buffer.from([1, 8, 1, 6, 64, 2, 104, 1, 252, 8, 58, 2]));
});
it("ZclFrame from buffer configReportRsp failed", () => {
const buffer = Buffer.from([0x08, 0x01, 0x07, 0x02, 0x01, 0x01, 0x01]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genPowerCfg.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
7,
);
const payload = [{status: 2, direction: 1, attrId: 257}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer defaultRsp", () => {
const buffer = Buffer.from([0x18, 0x04, 0x0b, 0x0c, 0x82]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
4,
11,
);
const payload = {cmdId: 12, statusCode: 130};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
test.each([
[
[
28, 95, 17, 3, 10, 1, 255, 66, 68, 3, 40, 29, 5, 33, 190, 45, 8, 33, 47, 18, 9, 33, 2, 21, 100, 16, 1, 101, 16, 0, 110, 32, 255, 111,
32, 255, 148, 32, 4, 149, 57, 184, 30, 21, 62, 150, 57, 211, 249, 17, 69, 151, 57, 0, 48, 104, 59, 152, 57, 0, 0, 0, 0, 155, 33, 1, 0,
156, 32, 1, 10, 33, 56, 38, 12, 40, 0, 0,
],
[
{
attrId: 65281,
dataType: 66,
attrData: {
"3": 29,
"5": 11710,
"8": 4655,
"9": 5378,
"10": 9784,
"12": 0,
"100": 1,
"101": 0,
"110": 255,
"111": 255,
"148": 4,
"149": 0.14562499523162842,
"150": 2335.614013671875,
"151": 0.0035429000854492188,
"152": 0,
"155": 1,
"156": 1,
},
},
],
],
[
[
28, 95, 17, 3, 10, 5, 0, 66, 21, 108, 117, 109, 105, 46, 115, 101, 110, 115, 111, 114, 95, 119, 108, 101, 97, 107, 46, 97, 113, 49, 1,
255, 66, 34, 1, 33, 213, 12, 3, 40, 33, 4, 33, 168, 19, 5, 33, 43, 0, 6, 36, 0, 0, 5, 0, 0, 8, 33, 4, 2, 10, 33, 0, 0, 100, 16, 0,
],
[
{attrId: 5, dataType: 66, attrData: "lumi.sensor_wleak.aq1"},
{attrId: 65281, dataType: 66, attrData: {"1": 3285, "3": 33, "4": 5032, "5": 43, "6": 327680, "8": 516, "10": 0, "100": 0}},
],
],
])("ZclFrame from buffer xiaomiStruct", (data, payload) => {
const buffer = Buffer.from(data);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: true,
},
4447,
3,
10,
);
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer struct", () => {
const buffer = Buffer.from([28, 52, 18, 194, 10, 2, 255, 76, 6, 0, 16, 1, 33, 206, 11, 33, 168, 67, 36, 1, 0, 0, 0, 0, 33, 48, 2, 32, 86]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: true,
},
4660,
194,
10,
);
const payload = [
{
attrId: 65282,
dataType: 76,
numElms: 6,
structElms: [
{elmType: 16, elmVal: 1},
{elmType: 33, elmVal: 3022},
{elmType: 33, elmVal: 17320},
{elmType: 36, elmVal: 1},
{elmType: 33, elmVal: 560},
{elmType: 32, elmVal: 86},
],
attrData: [
{elmType: 16, elmVal: 1},
{elmType: 33, elmVal: 3022},
{elmType: 33, elmVal: 17320},
{elmType: 36, elmVal: 1},
{elmType: 33, elmVal: 560},
{elmType: 32, elmVal: 86},
],
},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer discoverRsp", () => {
const buffer = Buffer.from([24, 23, 13, 0, 32, 0, 32, 33, 0, 32, 49, 0, 48, 51, 0, 32, 53, 0, 24]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genPowerCfg.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
23,
13,
);
const payload = {
discComplete: 0,
attrInfos: [
{attrId: 32, dataType: 32},
{attrId: 33, dataType: 32},
{attrId: 49, dataType: 48},
{attrId: 51, dataType: 32},
{attrId: 53, dataType: 24},
],
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
expect(frame.cluster.name).toEqual("genPowerCfg");
expect(frame.command.name).toEqual("discoverRsp");
});
it("ZclFrame from buffer error on malformed", () => {
const buffer = Buffer.from([0x08, 0x01]);
expect(() => {
Zcl.Frame.fromBuffer(Zcl.Clusters.genPowerCfg.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
}).toThrow("Invalid ZclHeader");
});
it("ZclFrame from buffer readRsp failed", () => {
const buffer = Buffer.from([8, 1, 1, 1, 0, 2]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
1,
);
const payload = [{status: 2, attrId: 1}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer readRsp success", () => {
const buffer = Buffer.from([8, 1, 1, 1, 0, 0, 32, 3]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
1,
);
const payload = [{status: Zcl.Status.SUCCESS, attrId: 1, dataType: Zcl.DataType.UINT8, attrData: 3}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer GPD commission", () => {
const buffer = Buffer.from([
0x11, 0x00, 0x04, 0x00, 0x00, 0xfe, 0xf4, 0x46, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xe0, 0x1b, 0x02, 0x81, 0xf2, 0xf1, 0xec, 0x92, 0xab, 0xff,
0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab, 0xa4, 0x46, 0xd4, 0xd5, 0xf9, 0x01, 0x00, 0x00,
]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.greenPower.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
0,
4,
);
const payload = {
options: 0,
srcID: 4650238,
frameCounter: 249,
commandID: 224,
payloadSize: 27,
commandFrame: {
deviceID: 2,
options: 129,
extendedOptions: 242,
gpdClientClusters: Buffer.alloc(0),
gpdServerClusters: Buffer.alloc(0),
manufacturerID: 0,
modelID: 0,
numClientClusters: 0,
numServerClusters: 0,
securityKey: Buffer.from([0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab]),
keyMic: 3587458724,
outgoingCounter: 505,
applicationInfo: 0,
numGpdCommands: 0,
gpdCommandIdList: Buffer.alloc(0),
genericSwitchConfig: 0,
currentContactStatus: 0,
},
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer GPD scene 0", () => {
const buffer = Buffer.from([0x11, 0x00, 0x00, 0xa0, 0x14, 0xfe, 0xf4, 0x46, 0x00, 0xe5, 0x04, 0x00, 0x00, 0x10, 0xff]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.greenPower.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
0,
0,
);
const payload = {
srcID: 4650238,
commandFrame: {},
commandID: 16,
frameCounter: 1253,
options: 5280,
payloadSize: 255,
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer GPD with extra data", () => {
const buffer = Buffer.from([0x11, 0x00, 0x00, 0xa0, 0x14, 0xfe, 0xf4, 0x46, 0x00, 0xe5, 0x04, 0x00, 0x00, 0x10, 0xff, 0x01]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.greenPower.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
0,
0,
);
const payload = {
srcID: 4650238,
commandFrame: {raw: Buffer.from([1])},
commandID: 16,
frameCounter: 1253,
options: 5280,
payloadSize: 255,
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer GPD pairing", () => {
const buffer = Buffer.from([
0x19, 0x17, 0x01, 0x68, 0xe5, 0x00, 0xf8, 0x71, 0x71, 0x01, 0x47, 0x65, 0xa1, 0x1c, 0x00, 0x4b, 0x12, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x12,
0x00, 0x00, 0x09, 0x3c, 0xed, 0x1d, 0xbf, 0x25, 0x63, 0xf9, 0x29, 0x5c, 0x0d, 0x3d, 0x9f, 0xc5, 0x76, 0xe1, 0, 0, 0, 0, 0, 0,
]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.greenPower.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
23,
1,
);
const payload = {
options: 0x00e568,
srcID: 0x017171f8,
sinkIEEEAddr: "0x00124b001ca16547",
sinkNwkAddr: 0,
deviceID: 2,
frameCounter: 4636,
gpdKey: Buffer.from([0x09, 0x3c, 0xed, 0x1d, 0xbf, 0x25, 0x63, 0xf9, 0x29, 0x5c, 0x0d, 0x3d, 0x9f, 0xc5, 0x76, 0xe1]),
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer readRsp alias type", () => {
const buffer = Buffer.from([8, 1, 1, 1, 0, 0, 8, 3]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
1,
);
const payload = [{status: Zcl.Status.SUCCESS, attrId: 1, dataType: Zcl.DataType.DATA8, attrData: 3}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp server to client", () => {
const buffer = Buffer.from([8, 1, 6, 1, 1, 0, 10, 10]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
6,
);
const payload = [{attrId: 1, direction: 1, timeout: 2570}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp client to server analog", () => {
const buffer = Buffer.from([8, 1, 6, 0, 0, 1, 32, 1, 0, 10, 0, 20]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
6,
);
const payload = [{attrId: 256, dataType: 32, direction: 0, maxRepIntval: 10, minRepIntval: 1, repChange: 20}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer configReportRsp client to server analog", () => {
const buffer = Buffer.from([8, 1, 6, 0, 0, 1, 8, 1, 0, 10, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
1,
6,
);
const payload = [{attrId: 256, dataType: 8, direction: 0, maxRepIntval: 10, minRepIntval: 1}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer readRsp", () => {
const buffer = Buffer.from([
24, 7, 1, 5, 0, 0, 66, 30, 84, 82, 65, 68, 70, 82, 73, 32, 98, 117, 108, 98, 32, 69, 50, 55, 32, 87, 83, 32, 111, 112, 97, 108, 32, 57,
56, 48, 108, 109, 6, 0, 0, 66, 8, 50, 48, 49, 55, 48, 51, 51, 49, 7, 0, 0, 48, 1, 10, 0, 0, 65, 15, 76, 69, 68, 49, 53, 52, 53, 71, 49,
50, 69, 50, 55, 69, 85,
]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 1,
disableDefaultResponse: true,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
7,
1,
);
const payload = [
{attrId: 5, status: 0, dataType: 66, attrData: "TRADFRI bulb E27 WS opal 980lm"},
{attrId: 6, status: 0, dataType: 66, attrData: "20170331"},
{attrId: 7, status: 0, dataType: 48, attrData: 1},
{attrId: 10, status: 0, dataType: 65, attrData: Buffer.from([76, 69, 68, 49, 53, 52, 53, 71, 49, 50, 69, 50, 55, 69, 85])},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame with Assa (manufacturer specific) cluster create", () => {
const payload = [{attrId: 0x0012, status: 0, attrData: 1, dataType: 32}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, false, 0x101d, 8, "readRsp", 0xfc00, payload, {});
expect(frame.cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("ZclFrame with Assa (manufacturer specific) cluster create with non Assamanufcode", () => {
const payload = [{attrId: 0x0012, status: 0, attrData: 1, dataType: 32}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, false, 0x10f3, 8, "readRsp", 0xfc00, payload, {});
expect(frame.cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("ZclFrame with Assa (manufacturer specific) cluster fromBuffer", () => {
const buffer = Buffer.from([0x04, 0xf2, 0x10, 0x08, 0x01, 0x00, 0x00, 0x00, 0x20, 0x01]);
const frame = Zcl.Frame.fromBuffer(0xfc00, Zcl.Header.fromBuffer(buffer)!, buffer, {});
expect(frame.cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("ZclFrame to buffer with reservered bits", () => {
const expected = Buffer.from([224, 8, 12, 0, 0, 240]);
const payload = {startAttrId: 0, maxAttrIds: 240};
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, false, undefined, 8, "discover", 0, payload, {}, 7);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame from buffer with reservered bits", () => {
const buffer = Buffer.from([224, 8, 12, 0, 0, 240]);
const frame = Zcl.Frame.fromBuffer(0, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 7,
direction: 0,
disableDefaultResponse: false,
frameType: 0,
manufacturerSpecific: false,
},
undefined,
8,
12,
);
const payload = {startAttrId: 0, maxAttrIds: 240};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame to buffer discover", () => {
const expected = Buffer.from([0, 8, 12, 0, 0, 240]);
const payload = {startAttrId: 0, maxAttrIds: 240};
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, false, undefined, 8, "discover", 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer queryNextImageResponse with non zero status", () => {
const expected = Buffer.from([9, 8, 2, 1]);
const payload = {status: 1};
const frame = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.SERVER_TO_CLIENT,
false,
undefined,
8,
"queryNextImageResponse",
25,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer queryNextImageResponse with zero status", () => {
const expected = Buffer.from([9, 8, 2, 0, 1, 0, 3, 0, 5, 0, 0, 0, 6, 0, 0, 0]);
const payload = {status: 0, manufacturerCode: 1, imageType: 3, fileVersion: 5, imageSize: 6};
const frame = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.SERVER_TO_CLIENT,
false,
undefined,
8,
"queryNextImageResponse",
25,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer queryNextImageResponse with zero status and missing parameters", () => {
const payload = {status: 0};
const frame = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.SERVER_TO_CLIENT,
false,
undefined,
8,
"queryNextImageResponse",
25,
payload,
{},
);
let error;
try {
frame.toBuffer();
} catch (e) {
error = e;
}
expect(error).toStrictEqual(new Error(`Parameter 'manufacturerCode' is missing`));
});
it("ZclFrame to buffer readRsp UTC", () => {
const expected = Buffer.from([24, 74, 1, 0, 0, 0, 226, 234, 83, 218, 36]);
const payload = [{attrId: 0, status: 0, attrData: 618288106, dataType: 226}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 74, "readRsp", 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer write Livolo malformed", () => {
// Created as example for https://github.com/Koenkk/zigbee-herdsman/issues/127
const expectedOn = Buffer.from([0x1c, 0xd2, 0x1a, 0xe9, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
const payloadOn = [{attrId: 1, attrData: Buffer.from([1, 0, 0, 0, 0, 0, 0, 0]), dataType: 1}];
const frameOn = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, 0x1ad2, 233, "write", 0, payloadOn, {});
expect(frameOn.toBuffer()).toStrictEqual(expectedOn);
const expectedOff = Buffer.from([0x1c, 0xd2, 0x1a, 0xe9, 0x02, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
const payloadOff = [{attrId: 1, attrData: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), dataType: 1}];
const frameOff = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, 0x1ad2, 233, "write", 0, payloadOff, {});
expect(frameOff.toBuffer()).toStrictEqual(expectedOff);
});
it("ZclFrame write request with string as bytes array", () => {
const payload = [{attrId: 0x0401, attrData: [0x07, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x14], dataType: 0x42}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, 0x115f, 15, "write", 0, payload, {});
const expected = [0x14, 0x5f, 0x11, 0x0f, 0x02, 0x01, 0x04, 0x42, 0x07, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x14];
expect(Buffer.from(expected)).toStrictEqual(frame.toBuffer());
});
it("ZclFrame write rsp", () => {
const payload = [{status: 0x11, attrId: 0x22}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, 0x115f, 15, "writeRsp", 0, payload, {});
const buffer = frame.toBuffer();
const expected = [0x14, 0x5f, 0x11, 0x0f, 0x04, 0x11, 0x22, 0x00];
expect(Buffer.from(expected)).toStrictEqual(buffer);
});
//{ frameType: 0, manufSpec: 0, direction: 0, disDefaultRsp: 0 } 0 8 'discover' { startAttrId: 0, maxAttrIds: 240 }
it("ZclFrame to buffer readRsp success", () => {
const expected = Buffer.from([8, 1, 1, 1, 0, 0, 32, 3]);
const payload = [{status: Zcl.Status.SUCCESS, attrId: 1, dataType: Zcl.DataType.UINT8, attrData: 3}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, false, undefined, 1, 1, 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer defaultRsp success", () => {
const expected = Buffer.from([0x18, 0x04, 0x0b, 0x0c, 0x82]);
const payload = {cmdId: 12, statusCode: 130};
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 4, 11, 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer readStructured single element", () => {
const expected = Buffer.from([0x18, 0x02, 0x0e, 0x01, 0x00, 0x01, 0x02, 0x00]);
const payload = [{attrId: 0x0001, selector: {indexes: [2]}}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 2, 0x0e, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer readStructured multiple elements", () => {
const expected = Buffer.from([
0x18, 0x02, 0x0e, 0x02, 0x00, 0x02, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x03, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
]);
const payload = [
{attrId: 0x0002, selector: {indexes: [3, 4]}},
{attrId: 0x0005, selector: {indexes: [6, 7, 8]}},
];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 2, 0x0e, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer readStructured whole attribute", () => {
const expected = Buffer.from([0x18, 0x02, 0x0e, 0x09, 0x00, 0x00]);
const payload = [{attrId: 0x0009, selector: {}}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 2, 0x0e, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame from buffer readStructured single elements", () => {
const buffer = Buffer.from([0x18, 0x02, 0x0e, 0x01, 0x00, 0x01, 0x02, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: true,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
2,
0x0e,
);
const payload = [{attrId: 0x0001, selector: {indexes: [2]}}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer readStructured multiple elements", () => {
const buffer = Buffer.from([
0x18, 0x02, 0x0e, 0x02, 0x00, 0x02, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x03, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: true,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
2,
0x0e,
);
const payload = [
{attrId: 0x0002, selector: {indexes: [3, 4]}},
{attrId: 0x0005, selector: {indexes: [6, 7, 8]}},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer readStructured whole attribute", () => {
const buffer = Buffer.from([0x18, 0x02, 0x0e, 0x09, 0x00, 0x00]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: true,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
2,
0x0e,
);
const payload = [{attrId: 0x0009, selector: {indicatorType: StructuredIndicatorType.Whole}}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame to buffer writeStructured single element", () => {
const expected = Buffer.from([0x10, 0x02, 0x0f, 0x01, 0x00, 0x01, 0x02, 0x00, 0x20, 0x03]);
const payload = [{attrId: 0x0001, selector: {indexes: [2]}, dataType: Zcl.DataType.UINT8, elementData: 3}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, undefined, 2, 0x0f, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructured multiple elements", () => {
const expected = Buffer.from([
0x10, 0x02, 0x0f, 0x02, 0x00, 0x02, 0x03, 0x00, 0x04, 0x00, 0x42, 0x03, 0x66, 0x6f, 0x6f, 0x05, 0x00, 0x03, 0x06, 0x00, 0x07, 0x00, 0x08,
0x00, 0x42, 0x03, 0x62, 0x61, 0x72,
]);
const payload = [
{attrId: 0x0002, selector: {indexes: [3, 4]}, dataType: Zcl.DataType.CHAR_STR, elementData: "foo"},
{attrId: 0x0005, selector: {indexes: [6, 7, 8]}, dataType: Zcl.DataType.CHAR_STR, elementData: "bar"},
];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, undefined, 2, 0x0f, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructured whole attribute", () => {
const expected = Buffer.from([0x10, 0x02, 0x0f, 0x09, 0x00, 0x00, 0x48, 0x20, 0x03, 0x00, 0x0a, 0x0b, 0x0c]);
const payload = [
{attrId: 0x0009, selector: {}, dataType: Zcl.DataType.ARRAY, elementData: {elementType: Zcl.DataType.UINT8, elements: [10, 11, 12]}},
];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, undefined, 2, 0x0f, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructured add element into set/bag", () => {
const expected = Buffer.from([0x10, 0x02, 0x0f, 0x0d, 0x00, 0x10, 0x42, 0x03, 0x66, 0x6f, 0x6f]);
const payload = [
{attrId: 0x000d, selector: {indicatorType: StructuredIndicatorType.WriteAdd}, dataType: Zcl.DataType.CHAR_STR, elementData: "foo"},
];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, undefined, 2, 0x0f, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructured remove element from set/bag", () => {
const expected = Buffer.from([0x10, 0x02, 0x0f, 0x0e, 0x00, 0x20, 0x42, 0x03, 0x62, 0x61, 0x72]);
const payload = [
{attrId: 0x000e, selector: {indicatorType: StructuredIndicatorType.WriteRemove}, dataType: Zcl.DataType.CHAR_STR, elementData: "bar"},
];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.CLIENT_TO_SERVER, true, undefined, 2, 0x0f, Zcl.Clusters.genBasic.ID, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructured Livolo malformed", () => {
const expected = Buffer.from([0x7c, 0xd2, 0x1a, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
const payload = [{attrId: 0x0000, selector: null, elementData: [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]}];
const frame = Zcl.Frame.create(
FrameType.GLOBAL,
Direction.SERVER_TO_CLIENT,
true,
0x1ad2,
0xe9,
0x0f,
Zcl.Clusters.genBasic.ID,
payload,
{},
3,
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructuredRsp success", () => {
const expected = Buffer.from([8, 1, 0x10, 0]);
const payload = [{status: Zcl.Status.SUCCESS}];
const frame = Zcl.Frame.create(
FrameType.GLOBAL,
Direction.SERVER_TO_CLIENT,
false,
undefined,
1,
0x10,
Zcl.Clusters.genBasic.ID,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructuredRsp failed selector unknown or none for type", () => {
const expected = Buffer.from([8, 1, 0x10, 147, 1, 0, 0]);
const payload = [{status: Zcl.Status.ACTION_DENIED, attrId: 1, selector: {indicatorType: StructuredIndicatorType.Whole}}];
const frame = Zcl.Frame.create(
FrameType.GLOBAL,
Direction.SERVER_TO_CLIENT,
false,
undefined,
1,
0x10,
Zcl.Clusters.genBasic.ID,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructuredRsp failed with selector", () => {
const expected = Buffer.from([8, 1, 0x10, 147, 1, 0, 1, 254, 0]);
const payload = [{status: Zcl.Status.ACTION_DENIED, attrId: 1, selector: {indexes: [254]}}];
const frame = Zcl.Frame.create(
FrameType.GLOBAL,
Direction.SERVER_TO_CLIENT,
false,
undefined,
1,
0x10,
Zcl.Clusters.genBasic.ID,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer writeStructuredRsp failed mixed selectors", () => {
const expected = Buffer.from([8, 1, 0x10, 147, 16, 0, 0, 149, 14, 0, 2, 254, 0, 32, 0]);
const payload = [
{status: Zcl.Status.ACTION_DENIED, attrId: 16, selector: {indicatorType: StructuredIndicatorType.Whole}},
{status: Zcl.Status.ABORT, attrId: 14, selector: {indexes: [254, 32]}},
];
const frame = Zcl.Frame.create(
FrameType.GLOBAL,
Direction.SERVER_TO_CLIENT,
false,
undefined,
1,
0x10,
Zcl.Clusters.genBasic.ID,
payload,
{},
);
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame from buffer writeStructuredRsp success", () => {
const buffer = Buffer.from([8, 1, 0x10, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: false,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
1,
0x10,
);
const payload = [{status: Zcl.Status.SUCCESS}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer writeStructuredRsp failed selector unknown or none for type", () => {
const buffer = Buffer.from([8, 1, 0x10, 147, 1, 0, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: false,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
1,
0x10,
);
const payload = [{status: Zcl.Status.ACTION_DENIED, attrId: 1, selector: {indicatorType: StructuredIndicatorType.Whole}}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer writeStructuredRsp failed with selector", () => {
const buffer = Buffer.from([8, 1, 0x10, 147, 1, 0, 1, 254, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: false,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
1,
0x10,
);
const payload = [{status: Zcl.Status.ACTION_DENIED, attrId: 1, selector: {indexes: [254]}}];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer writeStructuredRsp failed mixed selectors", () => {
const buffer = Buffer.from([8, 1, 0x10, 147, 16, 0, 0, 149, 14, 0, 2, 254, 0, 32, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.genBasic.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: Direction.SERVER_TO_CLIENT,
disableDefaultResponse: false,
frameType: FrameType.GLOBAL,
manufacturerSpecific: false,
},
undefined,
1,
0x10,
);
const payload = [
{status: Zcl.Status.ACTION_DENIED, attrId: 16, selector: {indicatorType: StructuredIndicatorType.Whole}},
{status: Zcl.Status.ABORT, attrId: 14, selector: {indexes: [254, 32]}},
];
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
});
it("ZclFrame from buffer ssIasAce arm command", () => {
const buffer = Buffer.from([1, 87, 0, 0, 6, 49, 50, 51, 52, 53, 54, 0]);
const frame = Zcl.Frame.fromBuffer(Zcl.Clusters.ssIasAce.ID, Zcl.Header.fromBuffer(buffer)!, buffer, {});
const header = new Zcl.Header(
{
reservedBits: 0,
direction: 0,
disableDefaultResponse: false,
frameType: 1,
manufacturerSpecific: false,
},
undefined,
87,
0,
);
const payload = {
armmode: 0,
code: "123456",
zoneid: 0,
};
expect(frame.header).toStrictEqual(header);
expect(frame.payload).toStrictEqual(payload);
expect(frame.header.isGlobal).toBe(false);
expect(frame.header.isSpecific).toBe(true);
expect(frame.isCluster("ssIasAce")).toBe(true);
expect(frame.isCommand("arm")).toBe(true);
});
it("ZclFrame to buffer discoverRsp", () => {
const expected = Buffer.from([24, 23, 13, 0, 32, 0, 32, 33, 0, 32, 49, 0, 48, 51, 0, 32, 53, 0, 24]);
const payload = {
discComplete: 0,
attrInfos: [
{attrId: 32, dataType: 32},
{attrId: 33, dataType: 32},
{attrId: 49, dataType: 48},
{attrId: 51, dataType: 32},
{attrId: 53, dataType: 24},
],
};
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, true, undefined, 23, 13, 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer tradfriArrowSingle", () => {
const expected = Buffer.from([0x05, 0x7c, 0x11, 0x1d, 0x07, 0x00, 0x01, 0x0d, 0x00]);
const payload = {value: 256, value2: 13};
const frame = Zcl.Frame.create(FrameType.SPECIFIC, Direction.CLIENT_TO_SERVER, false, 4476, 29, 7, 5, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer readRsp failed", () => {
const expected = Buffer.from([8, 1, 1, 1, 0, 2]);
const payload = [{status: 2, attrId: 1}];
const frame = Zcl.Frame.create(FrameType.GLOBAL, Direction.SERVER_TO_CLIENT, false, undefined, 1, 1, 0, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer offWithEffect", () => {
const expected = Buffer.from([0x1, 1, 64, 1, 0]);
const payload = {effectid: 1, effectvariant: 0};
const frame = Zcl.Frame.create(FrameType.SPECIFIC, Direction.CLIENT_TO_SERVER, false, undefined, 1, 64, 6, payload, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer offWithEffect", () => {
const expected = Buffer.from([9, 9, 0, 1]);
const frame = Zcl.Frame.create(FrameType.SPECIFIC, Direction.SERVER_TO_CLIENT, false, undefined, 9, "restartDeviceRsp", 21, {status: 1}, {});
expect(frame.toBuffer()).toStrictEqual(expected);
});
it("ZclFrame to buffer invalid frametype", () => {
expect(() => {
Zcl.Frame.create(
// @ts-expect-error invalid on purpose
3,
Direction.CLIENT_TO_SERVER,
false,
undefined,
1,
64,
6,
{},
{},
).toBuffer();
}).toThrow("Frametype '3' not valid");
});
it("BuffaloZcl read array", () => {
const buffer = Buffer.from([32, 3, 0, 1, 2, 3]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.ARRAY, {});
expect(buffalo.getPosition()).toBe(6);
expect(value).toStrictEqual([1, 2, 3]);
});
it("BuffaloZcl read struct", () => {
const buffer = Buffer.from([0, 2, 0, 32, 8, 33, 4, 0]);
const buffalo = new BuffaloZcl(buffer, 1);
const value = buffalo.read(DataType.STRUCT, {});
expect(buffalo.getPosition()).toBe(8);
expect(value).toStrictEqual([
{elmType: 32, elmVal: 8},
{elmType: 33, elmVal: 4},
]);
});
it("BuffaloZcl read longCharStr", () => {
const buffer = Buffer.from([5, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.LONG_CHAR_STR, {});
expect(buffalo.getPosition()).toBe(7);
expect(value).toStrictEqual("hello");
});
it("BuffaloZcl read longOctetStr", () => {
const buffer = Buffer.from([5, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.LONG_OCTET_STR, {});
expect(buffalo.getPosition()).toBe(7);
expect(value).toStrictEqual(buffer.subarray(2));
});
it("BuffaloZcl read extensionFieldSets", () => {
const buffer = Buffer.from([0, 3, 8, 1, 0, 2, 0, 3, 0, 4, 5]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(BuffaloZclDataType.EXTENSION_FIELD_SETS, {});
expect(buffalo.getPosition()).toBe(11);
expect(value).toStrictEqual([{clstId: 768, len: 8, extField: [1, 2, 3, 4, 5]}]);
});
it("BuffaloZcl read list zoneinfo", () => {
const buffer = Buffer.from([1, 5, 0, 2, 6, 0]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(BuffaloZclDataType.LIST_ZONEINFO, {length: 2});
expect(buffalo.getPosition()).toBe(6);
expect(value).toStrictEqual([
{zoneID: 1, zoneStatus: 5},
{zoneID: 2, zoneStatus: 6},
]);
});
it.each([DataType.UINT40, DataType.DATA40, DataType.BITMAP40])("BuffaloZcl read uint40, data40, bitmap40", (type) => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(type, {});
expect(buffalo.getPosition()).toBe(5);
expect(value).toStrictEqual(25853953281);
});
it.each([DataType.UINT48, DataType.DATA48, DataType.BITMAP48])("BuffaloZcl read uint48, data48, bitmap48", (type) => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(type, {});
expect(buffalo.getPosition()).toBe(6);
expect(value).toStrictEqual(7722435347713);
});
it.each([DataType.UINT56, DataType.DATA56, DataType.BITMAP56])("BuffaloZcl read uint56, data56, bitmap56", (type) => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7, 7]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(type, {});
expect(buffalo.getPosition()).toBe(7);
expect(value).toStrictEqual(1978047272322305n);
});
it.each([DataType.UINT64, DataType.DATA64, DataType.BITMAP64])("BuffaloZcl read uint64, data64, bitmap64", (type) => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7, 7, 9]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(type, {});
expect(buffalo.getPosition()).toBe(8);
expect(value).toStrictEqual(0x0907070605040501n);
});
it("BuffaloZcl read int40", () => {
const buffer = Buffer.from([254, 255, 255, 255, 127]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.INT40, {});
expect(buffalo.getPosition()).toBe(5);
expect(value).toStrictEqual(549755813886);
});
it("BuffaloZcl read int56", () => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7, 7]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.INT56, {});
expect(buffalo.getPosition()).toBe(7);
expect(value).toStrictEqual(1978047272322305n);
});
it("BuffaloZcl read int64", () => {
const buffer = Buffer.from([1, 5, 4, 5, 6, 7, 7, 9]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(DataType.INT64, {});
expect(buffalo.getPosition()).toBe(8);
expect(value).toStrictEqual(650496393613673729n);
});
it.each([
["no data point", Buffer.from([]), []],
["single data point", Buffer.from([1, 4, 0, 1, 1]), [{dp: 1, datatype: 4, data: Buffer.from([1])}]],
[
"two data points",
Buffer.from([1, 4, 0, 1, 1, 4, 2, 0, 4, 0, 0, 0, 90]),
[
{dp: 1, datatype: 4, data: Buffer.from([1])},
{dp: 4, datatype: 2, data: Buffer.from([0, 0, 0, 90])},
],
],
["incomplete data point is ignored", Buffer.from([1, 4, 0, 1, 1, 4]), [{dp: 1, datatype: 4, data: Buffer.from([1])}]],
[
"incomplete data buffer",
Buffer.from([1, 4, 0, 1, 1, 4, 2, 0, 4, 0, 0, 0]),
[
{dp: 1, datatype: 4, data: Buffer.from([1])},
{dp: 4, datatype: 2, data: Buffer.from([0, 0, 0])},
],
],
])("BuffaloZcl read readListTuyaDataPointValues %s", (_name, buffer, payload) => {
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES, {});
expect(buffalo.isMore()).not.toBeTruthy();
expect(value).toStrictEqual(payload);
});
it("BuffaloZcl write charStr", () => {
const payload = "hello";
const buffer = Buffer.alloc(7);
const expected = Buffer.from([5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.CHAR_STR, payload, {});
expect(buffalo.getPosition()).toBe(6);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write longCharStr", () => {
const payload = "hello";
const buffer = Buffer.alloc(7);
const expected = Buffer.from([5, 0, 0x68, 0x65, 0x6c, 0x6c, 0x6f]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.LONG_CHAR_STR, payload, {});
expect(buffalo.getPosition()).toBe(7);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write octetStr", () => {
const payload = [1, 2, 4];
const buffer = Buffer.alloc(5);
const expected = Buffer.from([3, 1, 2, 4, 0]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.OCTET_STR, payload, {});
expect(buffalo.getPosition()).toBe(4);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write longOctetStr", () => {
const payload = [1, 2, 3, 4, 5];
const buffer = Buffer.alloc(7);
const expected = Buffer.from([5, 0, 1, 2, 3, 4, 5]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.LONG_OCTET_STR, payload, {});
expect(buffalo.getPosition()).toBe(7);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write extensionFieldSets", () => {
const payload = [{clstId: 768, len: 8, extField: [1, 2, 3, 4, 5]}];
const buffer = Buffer.alloc(12);
const expected = Buffer.from([0, 3, 8, 1, 0, 2, 0, 3, 0, 4, 5, 0]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.EXTENSION_FIELD_SETS, payload, {});
expect(buffalo.getPosition()).toBe(11);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write zoneinfo", () => {
const payload = [
{zoneID: 1, zoneStatus: 5},
{zoneID: 2, zoneStatus: 6},
];
const buffer = Buffer.alloc(6);
const expected = Buffer.from([1, 5, 0, 2, 6, 0]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.LIST_ZONEINFO, payload, {});
expect(buffalo.getPosition()).toBe(6);
expect(buffer).toStrictEqual(expected);
});
it.each([DataType.UINT40, DataType.DATA40, DataType.BITMAP40])("BuffaloZcl write uint40, data40, bitmap40", (type) => {
const payload = 1099511627773;
const buffer = Buffer.alloc(5);
const expected = Buffer.from([253, 255, 255, 255, 255]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(type, payload, {});
expect(buffalo.getPosition()).toBe(5);
expect(buffer).toStrictEqual(expected);
});
it.each([DataType.UINT48, DataType.DATA48, DataType.BITMAP48])("BuffaloZcl write uint48, data48, bitmap48", (type) => {
const payload = 281474976710653;
const buffer = Buffer.alloc(6);
const expected = Buffer.from([253, 255, 255, 255, 255, 255]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(type, payload, {});
expect(buffalo.getPosition()).toBe(6);
expect(buffer).toStrictEqual(expected);
});
it.each([DataType.UINT56, DataType.DATA56, DataType.BITMAP56])("BuffaloZcl write uint56, data56, bitmap56", (type) => {
const payload = 1978047272322305n;
const buffer = Buffer.alloc(7);
const expected = Buffer.from([1, 5, 4, 5, 6, 7, 7]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(type, payload, {});
expect(buffalo.getPosition()).toBe(7);
expect(buffer).toStrictEqual(expected);
});
it.each([DataType.UINT64, DataType.DATA64, DataType.BITMAP64])("BuffaloZcl write uint64, data64, bitmap64", (type) => {
const payload = 0x0907070605040501n;
const buffer = Buffer.alloc(8);
const expected = Buffer.from([1, 5, 4, 5, 6, 7, 7, 9]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(type, payload, {});
expect(buffalo.getPosition()).toBe(8);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write int40", () => {
const payload = 549755813886;
const buffer = Buffer.alloc(5);
const expected = Buffer.from([254, 255, 255, 255, 127]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.INT40, payload, {});
expect(buffalo.getPosition()).toBe(5);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write int56", () => {
const payload = 1978047272322305n;
const buffer = Buffer.alloc(7);
const expected = Buffer.from([1, 5, 4, 5, 6, 7, 7]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.INT56, payload, {});
expect(buffalo.getPosition()).toBe(7);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write int64", () => {
const payload = 650496393613673729n;
const buffer = Buffer.alloc(8);
const expected = Buffer.from([1, 5, 4, 5, 6, 7, 7, 9]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.INT64, payload, {});
expect(buffalo.getPosition()).toBe(8);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write array element type string", () => {
const payload = {
elementType: Zcl.DataType.OCTET_STR,
elements: [
[0, 13, 1, 6, 0, 2],
[1, 13, 2, 6, 0, 2],
[2, 13, 3, 6, 0, 2],
[3, 13, 4, 6, 0, 2],
],
};
const expected = Buffer.from([0x41, 0x04, 0x00, 6, 0, 13, 1, 6, 0, 2, 6, 1, 13, 2, 6, 0, 2, 6, 2, 13, 3, 6, 0, 2, 6, 3, 13, 4, 6, 0, 2]);
const buffer = Buffer.alloc(expected.length);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.ARRAY, payload, {});
expect(buffalo.getPosition()).toBe(expected.length);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write array element type numeric", () => {
const payload = {elementType: Zcl.DataType.DATA8, elements: [0, 0, 0, 0]};
const expected = Buffer.from([0x08, 0x04, 0x00, 0, 0, 0, 0]);
const buffer = Buffer.alloc(expected.length);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.ARRAY, payload, {});
expect(buffalo.getPosition()).toBe(expected.length);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write array element type numeric passed as string of DataType", () => {
const payload = {elementType: "DATA8", elements: [0, 0, 0, 0]};
const expected = Buffer.from([0x08, 0x04, 0x00, 0, 0, 0, 0]);
const buffer = Buffer.alloc(expected.length);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.ARRAY, payload, {});
expect(buffalo.getPosition()).toBe(expected.length);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write struct", () => {
const payload = [
{elmType: Zcl.DataType.UINT8, elmVal: 3},
{elmType: Zcl.DataType.CHAR_STR, elmVal: "a"},
];
const expected = Buffer.from([2, 0, Zcl.DataType.UINT8, 3, Zcl.DataType.CHAR_STR, 1, 0x61]);
const buffer = Buffer.alloc(expected.length);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.STRUCT, payload, {});
expect(buffalo.getPosition()).toBe(expected.length);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write security key", () => {
const payload = [0x09, 0x07, 0x07, 0x06, 0x05, 0x04, 0x05, 0x01, 0x09, 0x07, 0x07, 0x06, 0x05, 0x04, 0x05, 0x01];
const buffer = Buffer.alloc(16);
const expected = Buffer.from([0x09, 0x07, 0x07, 0x06, 0x05, 0x04, 0x05, 0x01, 0x09, 0x07, 0x07, 0x06, 0x05, 0x04, 0x05, 0x01]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(DataType.SEC_KEY, payload, {});
expect(buffalo.getPosition()).toBe(16);
expect(buffer).toStrictEqual(expected);
});
it.each([
["no data point", [], Buffer.from([])],
["single data point", [{dp: 1, datatype: 4, data: Buffer.from([1])}], Buffer.from([1, 4, 0, 1, 1])],
[
"two data points",
[
{dp: 1, datatype: 4, data: Buffer.from([1])},
{dp: 4, datatype: 2, data: Buffer.from([0, 0, 0, 90])},
],
Buffer.from([1, 4, 0, 1, 1, 4, 2, 0, 4, 0, 0, 0, 90]),
],
])("BuffaloZcl writeListTuyaDataPointValues %s", (_name, payload, expected) => {
const buffer = Buffer.alloc(expected.length);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.LIST_TUYA_DATAPOINT_VALUES, payload, {});
expect(buffalo.getPosition()).toBe(expected.length);
expect(buffer).toStrictEqual(expected);
});
it("Zcl utils get cluster without manufacturerCode", () => {
const cluster = Zcl.Utils.getCluster(0xfc00, undefined, {});
expect(cluster.ID).toBe(0xfc00);
expect(cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("Zcl utils get cluster with manufacturerCode", () => {
const cluster = Zcl.Utils.getCluster(0xfc00, 0x100b, {});
expect(cluster.ID).toBe(0xfc00);
expect(cluster.name).toBe("manuSpecificPhilips");
});
it("Zcl utils get cluster manufacturerCode", () => {
const cluster = Zcl.Utils.getCluster(0xfc00, 0x10f2, {});
expect(cluster.ID).toBe(0xfc00);
expect(cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("Zcl utils get cluster manufacturerCode wrong", () => {
const cluster = Zcl.Utils.getCluster(0xfc00, 123, {});
expect(cluster.ID).toBe(0xfc00);
expect(cluster.name).toBe("manuSpecificAssaDoorLock");
});
it("Zcl utils get cluster attributes manufacturerCode", () => {
const cluster = Zcl.Utils.getCluster("closuresWindowCovering", 0x1021, {});
const attribute = cluster.getAttribute(0xf004);
expect(attribute).toStrictEqual({ID: 0xf004, manufacturerCode: 0x1021, name: "stepPositionTilt", type: 48});
});
it("Zcl utils get cluster attributes manufacturerCode wrong", () => {
const cluster = Zcl.Utils.getCluster("closuresWindowCovering", 123, {});
expect(() => cluster.getAttribute(0x1000)).toThrow("Cluster 'closuresWindowCovering' has no attribute '4096'");
});
it("Zcl utils get command", () => {
const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {});
const command = cluster.getCommand(0);
expect(command.name).toEqual("off");
expect(cluster.getCommand("off")).toEqual(command);
});
it("Zcl utils get attribute", () => {
const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {});
const command = cluster.getAttribute(16385);
expect(command.name).toEqual("onTime");
expect(cluster.getAttribute("onTime")).toEqual(command);
});
it("Zcl utils get attribute non-existing", () => {
const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {});
expect(() => cluster.getAttribute("notExisting")).toThrow("Cluster 'genOnOff' has no attribute 'notExisting'");
});
it("Zcl utils get command non-existing", () => {
const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {});
expect(() => cluster.getCommand("notExisting")).toThrow("Cluster 'genOnOff' has no command 'notExisting'");
});
it("Zcl green power readGpd commissioning", () => {
const buffer = [
0xff, // device
0x00, // options
];
const frame = new BuffaloZcl(Buffer.from(buffer));
expect(
frame.read(BuffaloZclDataType.GPD_FRAME, {
payload: {
commandID: 0xe0,
},
}),
).toStrictEqual({
deviceID: 0xff,
options: 0x00,
extendedOptions: 0x00,
securityKey: Buffer.alloc(16),
keyMic: 0,
outgoingCounter: 0,
manufacturerID: 0,
modelID: 0,
numGpdCommands: 0,
gpdCommandIdList: Buffer.alloc(0),
numServerClusters: 0,
numClientClusters: 0,
gpdServerClusters: Buffer.alloc(0),
gpdClientClusters: Buffer.alloc(0),
applicationInfo: 0x00,
genericSwitchConfig: 0,
currentContactStatus: 0,
});
});
it("Zcl green power readGpd commissioning all options", () => {
const buffer = [
0xff, // device
0x80 | 0x04, // options
0x20 | 0x40 | 0x80, // extended options
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0, // security key
0,
0,
0,
0, // key mic
0,
0,
0,
0, // outgoing counter
0x01 | 0x02 | 0x04 | 0x08 | 0x10, // application info
0,
0, // manufacturer ID
0,
0, // model ID
0, // num GPD commands + commands
0, // clusters
2, // switch info length
0, // generic switch config
1, // current contact status
];
const frame = new BuffaloZcl(Buffer.from(buffer));
expect(
frame.read(BuffaloZclDataType.GPD_FRAME, {
payload: {
commandID: 0xe0,
},
}),
).toStrictEqual({
deviceID: 0xff,
options: 0x80 | 0x04,
extendedOptions: 0x20 | 0x40 | 0x80,
securityKey: Buffer.alloc(16),
keyMic: 0,
outgoingCounter: 0,
manufacturerID: 0,
modelID: 0,
numGpdCommands: 0,
gpdCommandIdList: Buffer.alloc(0),
numServerClusters: 0,
numClientClusters: 0,
gpdServerClusters: Buffer.alloc(0),
gpdClientClusters: Buffer.alloc(0),
applicationInfo: 0x01 | 0x02 | 0x04 | 0x08 | 0x10,
genericSwitchConfig: 0,
currentContactStatus: 1,
});
});
it("Zcl green power readGpd channel request", () => {
const buffer = [0xfa];
const frame = new BuffaloZcl(Buffer.from(buffer));
expect(
frame.read(BuffaloZclDataType.GPD_FRAME, {
payload: {
commandID: 0xe3,
},
}),
).toStrictEqual({
nextChannel: 0xa,
nextNextChannel: 0xf,
});
});
it("Zcl green power readGpd attribute report", () => {
const buffer = [
0x12,
0x34, // Manufacturer ID
0xff,
0xff, // Cluster ID
0x00,
0x00, // Attribute ID
DataType.UINT32, // Attribute Type
0x00,
0x01,
0x02,
0x03,
0x01,
0x00,
DataType.CHAR_STR,
0x06,
0x5a,
0x49,
0x47,
0x42,
0x45,
0x45,
0x02,
0x00,
DataType.BOOLEAN,
0x01,
];
const frame = new BuffaloZcl(Buffer.from(buffer));
expect(
frame.read(BuffaloZclDataType.GPD_FRAME, {
payload: {
commandID: 0xa1,
payloadSize: buffer.length,
},
}),
).toStrictEqual({
manufacturerCode: 13330,
clusterID: 65535,
attributes: {
"0": 50462976,
"1": "ZIGBEE",
"2": 1,
},
});
});
it("Zcl green power writeGpd commissioning", () => {
const expected = [
1, // length
0, // options
];
const frame = new BuffaloZcl(Buffer.alloc(2));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0xf0,
options: 0,
panID: 0,
securityKey: Buffer.alloc(16),
keyMic: 0,
frameCounter: 0,
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.from(expected));
});
it("Zcl green power writeGpd commissioning all options", () => {
const expected = [
27, // length
0b11111, // options
0xff,
0xff, // PAN ID
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0, // security key
0,
0,
0,
0, // key mic
0,
0,
0,
0, // frame counter
];
const frame = new BuffaloZcl(Buffer.alloc(28));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0xf0,
options: 0b11111,
panID: 0xffff,
securityKey: Buffer.alloc(16),
keyMic: 0,
frameCounter: 0,
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.from(expected));
});
it("Zcl green power writeGpd custom reply", () => {
const expected = [
6, // length
90,
73,
71,
66,
69,
69, // ZIGBEE
];
const frame = new BuffaloZcl(Buffer.alloc(7));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0xf4,
buffer: Buffer.from("ZIGBEE"),
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.from(expected));
});
it("Zcl green power writeGpd unhandled command", () => {
const frame = new BuffaloZcl(Buffer.alloc(7));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0x1ff,
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.alloc(0));
});
it("Zcl green power writeGpd channel configuration", () => {
const expected = [
1, // length
0xf, // Channel 26
];
const frame = new BuffaloZcl(Buffer.alloc(2));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0xf3,
operationalChannel: 0xf,
basic: false,
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.from(expected));
});
it("Zcl green power writeGpd channel configuration basic", () => {
const expected = [
1, // length
0x1f, // Channel 26 + Basic
];
const frame = new BuffaloZcl(Buffer.alloc(2));
frame.write(
BuffaloZclDataType.GPD_FRAME,
{
commandID: 0xf3,
operationalChannel: 0xf,
basic: true,
},
{},
);
expect(frame.getWritten()).toStrictEqual(Buffer.from(expected));
});
it("ZclFrame parse MiBoxer zone configuration command", () => {
const zoneConfigPayload = Buffer.from([
0x11, 0x01, 0xf0, 0x08, 0x84, 0x2b, 0x01, 0x98, 0x2b, 0x02, 0xac, 0x2b, 0x03, 0xc0, 0x2b, 0x04, 0xd4, 0x2b, 0x05, 0xe8, 0x2b, 0x06, 0xfc,
0x2b, 0x07, 0x10, 0x2c, 0x08,
]);
const zoneConfigFrame = Zcl.Frame.fromBuffer(Zcl.Clusters.genGroups.ID, Zcl.Header.fromBuffer(zoneConfigPayload)!, zoneConfigPayload, {});
expect(zoneConfigFrame.payload.zones).toStrictEqual([
{zoneNum: 1, groupId: 0x2b84},
{zoneNum: 2, groupId: 0x2b98},
{zoneNum: 3, groupId: 0x2bac},
{zoneNum: 4, groupId: 0x2bc0},
{zoneNum: 5, groupId: 0x2bd4},
{zoneNum: 6, groupId: 0x2be8},
{zoneNum: 7, groupId: 0x2bfc},
{zoneNum: 8, groupId: 0x2c10},
]);
});
it("ZclFrame serialize MiBoxer zone configuration command", () => {
const testZones = [
{zoneNum: 1, groupId: 0x2b84},
{zoneNum: 2, groupId: 0x2b98},
{zoneNum: 3, groupId: 0x2bac},
{zoneNum: 4, groupId: 0x2bc0},
{zoneNum: 5, groupId: 0x2bd4},
{zoneNum: 6, groupId: 0x2be8},
{zoneNum: 7, groupId: 0x2bfc},
{zoneNum: 8, groupId: 0x2c10},
];
const zoneConfigFrame = Zcl.Frame.create(
FrameType.SPECIFIC,
Direction.CLIENT_TO_SERVER,
true,
undefined,
1,
"miboxerSetZones",
Zcl.Clusters.genGroups.ID,
{zones: testZones},
{},
);
expect(zoneConfigFrame.toBuffer()).toStrictEqual(
Buffer.from([
0x11, 0x01, 0xf0, 0x08, 0x84, 0x2b, 0x01, 0x98, 0x2b, 0x02, 0xac, 0x2b, 0x03, 0xc0, 0x2b, 0x04, 0xd4, 0x2b, 0x05, 0xe8, 0x2b, 0x06,
0xfc, 0x2b, 0x07, 0x10, 0x2c, 0x08,
]),
);
});
it("BuffaloZcl read BIG_ENDIAN_UINT24", () => {
const buffer = Buffer.from([0x01, 0x01, 0x86, 0xa0, 0x02]);
const buffalo = new BuffaloZcl(buffer, 1);
const value = buffalo.read(BuffaloZclDataType.BIG_ENDIAN_UINT24, {});
expect(buffalo.getPosition()).toBe(4);
expect(value).toStrictEqual(100000);
});
it("BuffaloZcl write BIG_ENDIAN_UINT24", () => {
const payload = 16777200;
const buffer = Buffer.alloc(4);
const expected = Buffer.from([0x00, 0xff, 0xff, 0xf0]);
const buffalo = new BuffaloZcl(buffer, 1);
buffalo.write(BuffaloZclDataType.BIG_ENDIAN_UINT24, payload, {});
expect(buffalo.getPosition()).toBe(4);
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl read BUFFER whole", () => {
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(BuffaloZclDataType.BUFFER, {});
expect(value).toStrictEqual(Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]));
});
it("BuffaloZcl read BUFFER with length", () => {
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]);
const buffalo = new BuffaloZcl(buffer);
const value = buffalo.read(BuffaloZclDataType.BUFFER, {length: 3});
expect(value).toStrictEqual(Buffer.from([0x01, 0x02, 0x03]));
});
it("BuffaloZcl write BUFFER whole", () => {
const buffer = Buffer.alloc(5);
const expected = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.BUFFER, expected, {});
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write BUFFER always whole - length ignored", () => {
const buffer = Buffer.alloc(5);
const expected = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.BUFFER, expected, {length: 3});
expect(buffer).toStrictEqual(expected);
});
it("BuffaloZcl write non existing type", () => {
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(0));
// @ts-expect-error invalid typing
buffalo.write(9999, 1, {});
}).toThrow(new Error("Write for '9999' not available"));
});
it("BuffaloZcl read non existing type", () => {
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(0));
// @ts-expect-error invalid typing
buffalo.read(9999, {});
}).toThrow(new Error("Read for '9999' not available"));
});
it("BuffaloZcl write TOD", () => {
const buffer = Buffer.alloc(4);
const expected = Buffer.from([0, 59, 34, 88]);
const buffalo = new BuffaloZcl(buffer);
const payload = {hours: 0, minutes: 59, seconds: 34, hundredths: 88};
buffalo.write(DataType.TOD, payload, {});
expect(buffalo.getBuffer()).toStrictEqual(expected);
});
it("BuffaloZcl read TOD", () => {
const expected = {hours: 0, minutes: 59, seconds: 34, hundredths: 88};
const buffalo = new BuffaloZcl(Buffer.from([0, 59, 34, 88]));
const value = buffalo.read(DataType.TOD, {});
expect(value).toStrictEqual(expected);
});
it("BuffaloZcl write DATE", () => {
const buffer = Buffer.alloc(4);
const expected = Buffer.from([100, 8, 31, 3]);
const buffalo = new BuffaloZcl(buffer);
const payload = {year: 2000, month: 8, dayOfMonth: 31, dayOfWeek: 3};
buffalo.write(DataType.DATE, payload, {});
expect(buffalo.getBuffer()).toStrictEqual(expected);
});
it("BuffaloZcl read DATE", () => {
const expected = {year: 2000, month: 8, dayOfMonth: 31, dayOfWeek: 3};
const buffalo = new BuffaloZcl(Buffer.from([100, 8, 31, 3]));
const value = buffalo.read(DataType.DATE, {});
expect(value).toStrictEqual(expected);
});
it("BuffaloZcl write TOD non-value", () => {
const buffer = Buffer.alloc(4);
const expected = Buffer.from([0xff, 0xff, 0xff, 0xff]);
const buffalo = new BuffaloZcl(buffer);
const payload = {hours: undefined, minutes: undefined, seconds: undefined, hundredths: undefined};
buffalo.write(DataType.TOD, payload, {});
expect(buffalo.getBuffer()).toStrictEqual(expected);
});
it("BuffaloZcl read DATE non-value", () => {
const buffer = Buffer.alloc(4);
const expected = Buffer.from([0xff, 0xff, 0xff, 0xff]);
const buffalo = new BuffaloZcl(buffer);
const payload = {year: undefined, month: undefined, dayOfMonth: undefined, dayOfWeek: undefined};
buffalo.write(DataType.DATE, payload, {});
expect(buffalo.getBuffer()).toStrictEqual(expected);
});
it("BuffaloZcl read OCTET_STR non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff]));
const value = buffalo.read(DataType.OCTET_STR, {});
expect(value).toStrictEqual(Buffer.from([]));
});
it("BuffaloZcl read CHAR_STR non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff]));
const value = buffalo.read(DataType.CHAR_STR, {});
expect(value).toStrictEqual("");
});
it("BuffaloZcl read MI_STRUCT non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff]));
const value = buffalo.read(BuffaloZclDataType.MI_STRUCT, {});
expect(value).toStrictEqual({});
});
it("BuffaloZcl read LONG_OCTET_STR non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff, 0xff]));
const value = buffalo.read(DataType.LONG_OCTET_STR, {});
expect(value).toStrictEqual(Buffer.from([]));
});
it("BuffaloZcl read LONG_CHAR_STR non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff, 0xff]));
const value = buffalo.read(DataType.LONG_CHAR_STR, {});
expect(value).toStrictEqual("");
});
it("BuffaloZcl read ARRAY non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([DataType.UINT8, 0xff, 0xff]));
const value = buffalo.read(DataType.ARRAY, {});
expect(value).toStrictEqual([]);
});
it("BuffaloZcl read STRUCT non-value", () => {
const buffalo = new BuffaloZcl(Buffer.from([0xff, 0xff]));
const value = buffalo.read(DataType.STRUCT, {});
expect(value).toStrictEqual([]);
});
it("BuffaloZcl write NO_DATA", () => {
const buffalo = new BuffaloZcl(Buffer.alloc(2));
const payload = null;
buffalo.write(DataType.NO_DATA, payload, {});
expect(buffalo.getPosition()).toStrictEqual(0);
expect(buffalo.getBuffer()).toStrictEqual(Buffer.from([0x00, 0x00]));
});
it("BuffaloZcl read NO_DATA", () => {
const buffalo = new BuffaloZcl(Buffer.from([0x00, 0x00]), 1);
const value = buffalo.read(DataType.NO_DATA, {});
expect(buffalo.getPosition()).toStrictEqual(1);
expect(value).toStrictEqual(undefined);
});
it("BuffaloZcl write UNKNOWN", () => {
const buffalo = new BuffaloZcl(Buffer.alloc(2));
const payload = null;
buffalo.write(DataType.UNKNOWN, payload, {});
expect(buffalo.getPosition()).toStrictEqual(0);
expect(buffalo.getBuffer()).toStrictEqual(Buffer.from([0x00, 0x00]));
});
it("BuffaloZcl read UNKNOWN", () => {
const buffalo = new BuffaloZcl(Buffer.from([0x00, 0x00]), 1);
const value = buffalo.read(DataType.UNKNOWN, {});
expect(buffalo.getPosition()).toStrictEqual(1);
expect(value).toStrictEqual(undefined);
});
it("ZclHeader return undefined when too short", () => {
const header = Zcl.Header.fromBuffer(Buffer.from([0, 8]));
expect(header).toStrictEqual(undefined);
});
it("ZclHeader return undefined when too short manufacturer specific", () => {
const header = Zcl.Header.fromBuffer(Buffer.from([4, 8, 3]));
expect(header).toStrictEqual(undefined);
});
describe("BuffaloZcl write passthrough to Buffalo", () => {
let buffalo: BuffaloZcl;
beforeEach(() => {
buffalo = new BuffaloZcl(Buffer.alloc(255));
});
it("uint8", () => {
const uint8Spy = vi.spyOn(buffalo, "writeUInt8");
const types = [DataType.DATA8, DataType.BOOLEAN, DataType.BITMAP8, DataType.UINT8, DataType.ENUM8];
let i = 0;
for (const type of types) {
buffalo.write(type, 1, {});
expect(uint8Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint16", () => {
const uint16Spy = vi.spyOn(buffalo, "writeUInt16");
const types = [DataType.DATA16, DataType.BITMAP16, DataType.UINT16, DataType.ENUM16, DataType.CLUSTER_ID, DataType.ATTR_ID];
let i = 0;
for (const type of types) {
buffalo.write(type, 1, {});
expect(uint16Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint24", () => {
const uint24Spy = vi.spyOn(buffalo, "writeUInt24");
const types = [DataType.DATA24, DataType.BITMAP24, DataType.UINT24];
let i = 0;
for (const type of types) {
buffalo.write(type, 1, {});
expect(uint24Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint32", () => {
const uint32Spy = vi.spyOn(buffalo, "writeUInt32");
const types = [DataType.DATA32, DataType.BITMAP32, DataType.UINT32, DataType.UTC, DataType.BAC_OID];
let i = 0;
for (const type of types) {
buffalo.write(type, 1, {});
expect(uint32Spy).toHaveBeenCalledTimes(++i);
}
});
it("int8", () => {
const int8Spy = vi.spyOn(buffalo, "writeInt8");
buffalo.write(DataType.INT8, 1, {});
expect(int8Spy).toHaveBeenCalledTimes(1);
});
it("int16", () => {
const int16Spy = vi.spyOn(buffalo, "writeInt16");
buffalo.write(DataType.INT16, 1, {});
expect(int16Spy).toHaveBeenCalledTimes(1);
});
it("int24", () => {
const int24Spy = vi.spyOn(buffalo, "writeInt24");
buffalo.write(DataType.INT24, 1, {});
expect(int24Spy).toHaveBeenCalledTimes(1);
});
it("int32", () => {
const int32Spy = vi.spyOn(buffalo, "writeInt32");
buffalo.write(DataType.INT32, 1, {});
expect(int32Spy).toHaveBeenCalledTimes(1);
});
it("int48", () => {
const int48Spy = vi.spyOn(buffalo, "writeInt48");
buffalo.write(DataType.INT48, 1, {});
expect(int48Spy).toHaveBeenCalledTimes(1);
});
it("list uint8", () => {
const listuint8Spy = vi.spyOn(buffalo, "writeListUInt8");
buffalo.write(BuffaloZclDataType.LIST_UINT8, [1], {});
expect(listuint8Spy).toHaveBeenCalledTimes(1);
});
it("list uint16", () => {
const listuint16Spy = vi.spyOn(buffalo, "writeListUInt16");
buffalo.write(BuffaloZclDataType.LIST_UINT16, [1], {});
expect(listuint16Spy).toHaveBeenCalledTimes(1);
});
it("list uint24", () => {
const listuint24Spy = vi.spyOn(buffalo, "writeListUInt24");
buffalo.write(BuffaloZclDataType.LIST_UINT24, [1], {});
expect(listuint24Spy).toHaveBeenCalledTimes(1);
});
it("list uint32", () => {
const listuint32Spy = vi.spyOn(buffalo, "writeListUInt32");
buffalo.write(BuffaloZclDataType.LIST_UINT32, [1], {});
expect(listuint32Spy).toHaveBeenCalledTimes(1);
});
// NOT SUPPORTED
// it('semi prec', () => {
// const semiPrecSpy = vi.spyOn(buffalo, 'writeSemiFloatLE');
// buffalo.write(DataType.SEMI_PREC, 1, {});
// expect(semiPrecSpy).toHaveBeenCalledTimes(1);
// });
it("single prec", () => {
const singlePrecSpy = vi.spyOn(buffalo, "writeFloatLE");
buffalo.write(DataType.SINGLE_PREC, 1.1, {});
expect(singlePrecSpy).toHaveBeenCalledTimes(1);
});
it("double prec", () => {
const doublePrecSpy = vi.spyOn(buffalo, "writeDoubleLE");
buffalo.write(DataType.DOUBLE_PREC, 1.1, {});
expect(doublePrecSpy).toHaveBeenCalledTimes(1);
});
it("ieee add", () => {
const doublePrecSpy = vi.spyOn(buffalo, "writeIeeeAddr");
buffalo.write(DataType.IEEE_ADDR, "0xf1f1f1f1f1f1f1f1", {});
expect(doublePrecSpy).toHaveBeenCalledTimes(1);
});
});
describe("BuffaloZcl read passthrough to Buffalo", () => {
let buffalo: BuffaloZcl;
beforeEach(() => {
buffalo = new BuffaloZcl(Buffer.alloc(255));
});
it("uint8", () => {
const uint8Spy = vi.spyOn(buffalo, "readUInt8");
const types = [DataType.DATA8, DataType.BOOLEAN, DataType.BITMAP8, DataType.UINT8, DataType.ENUM8];
let i = 0;
for (const type of types) {
buffalo.read(type, {});
expect(uint8Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint16", () => {
const uint16Spy = vi.spyOn(buffalo, "readUInt16");
const types = [DataType.DATA16, DataType.BITMAP16, DataType.UINT16, DataType.ENUM16, DataType.CLUSTER_ID, DataType.ATTR_ID];
let i = 0;
for (const type of types) {
buffalo.read(type, {});
expect(uint16Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint24", () => {
const uint24Spy = vi.spyOn(buffalo, "readUInt24");
const types = [DataType.DATA24, DataType.BITMAP24, DataType.UINT24];
let i = 0;
for (const type of types) {
buffalo.read(type, {});
expect(uint24Spy).toHaveBeenCalledTimes(++i);
}
});
it("uint32", () => {
const uint32Spy = vi.spyOn(buffalo, "readUInt32");
const types = [DataType.DATA32, DataType.BITMAP32, DataType.UINT32, DataType.UTC, DataType.BAC_OID];
let i = 0;
for (const type of types) {
buffalo.read(type, {});
expect(uint32Spy).toHaveBeenCalledTimes(++i);
}
});
it("int8", () => {
const int8Spy = vi.spyOn(buffalo, "readInt8");
buffalo.read(DataType.INT8, {});
expect(int8Spy).toHaveBeenCalledTimes(1);
});
it("int16", () => {
const int16Spy = vi.spyOn(buffalo, "readInt16");
buffalo.read(DataType.INT16, {});
expect(int16Spy).toHaveBeenCalledTimes(1);
});
it("int24", () => {
const int24Spy = vi.spyOn(buffalo, "readInt24");
buffalo.read(DataType.INT24, {});
expect(int24Spy).toHaveBeenCalledTimes(1);
});
it("int32", () => {
const int32Spy = vi.spyOn(buffalo, "readInt32");
buffalo.read(DataType.INT32, {});
expect(int32Spy).toHaveBeenCalledTimes(1);
});
it("int48", () => {
const int48Spy = vi.spyOn(buffalo, "readInt48");
buffalo.read(DataType.INT48, {});
expect(int48Spy).toHaveBeenCalledTimes(1);
});
it("list uint8", () => {
const listuint8Spy = vi.spyOn(buffalo, "readListUInt8");
buffalo.read(BuffaloZclDataType.LIST_UINT8, {length: 1});
expect(listuint8Spy).toHaveBeenCalledTimes(1);
});
it("list uint16", () => {
const listuint16Spy = vi.spyOn(buffalo, "readListUInt16");
buffalo.read(BuffaloZclDataType.LIST_UINT16, {length: 1});
expect(listuint16Spy).toHaveBeenCalledTimes(1);
});
it("list uint24", () => {
const listuint24Spy = vi.spyOn(buffalo, "readListUInt24");
buffalo.read(BuffaloZclDataType.LIST_UINT24, {length: 1});
expect(listuint24Spy).toHaveBeenCalledTimes(1);
});
it("list uint32", () => {
const listuint32Spy = vi.spyOn(buffalo, "readListUInt32");
buffalo.read(BuffaloZclDataType.LIST_UINT32, {length: 1});
expect(listuint32Spy).toHaveBeenCalledTimes(1);
});
// NOT SUPPORTED
// it('semi prec', () => {
// const semiPrecSpy = vi.spyOn(buffalo, 'readSemiFloatLE');
// buffalo.read(DataType.SEMI_PREC, {});
// expect(semiPrecSpy).toHaveBeenCalledTimes(1);
// });
it("single prec", () => {
const singlePrecSpy = vi.spyOn(buffalo, "readFloatLE");
buffalo.read(DataType.SINGLE_PREC, {});
expect(singlePrecSpy).toHaveBeenCalledTimes(1);
});
it("double prec", () => {
const doublePrecSpy = vi.spyOn(buffalo, "readDoubleLE");
buffalo.read(DataType.DOUBLE_PREC, {});
expect(doublePrecSpy).toHaveBeenCalledTimes(1);
});
it("ieee add", () => {
const doublePrecSpy = vi.spyOn(buffalo, "readIeeeAddr");
buffalo.read(DataType.IEEE_ADDR, {});
expect(doublePrecSpy).toHaveBeenCalledTimes(1);
});
});
it.each([
BuffaloZclDataType.LIST_UINT8,
BuffaloZclDataType.LIST_UINT16,
BuffaloZclDataType.LIST_UINT24,
BuffaloZclDataType.LIST_UINT32,
BuffaloZclDataType.LIST_ZONEINFO,
])("Throws when read is missing required length option - param %s", (type) => {
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(type, {});
}).toThrow(`Cannot read ${BuffaloZclDataType[type]} without length option specified`);
});
it("Reads as buffer when USE_DATA_TYPE is missing dataType option", () => {
const buffalo = new BuffaloZcl(Buffer.from([12, 34]));
const value = buffalo.read(BuffaloZclDataType.USE_DATA_TYPE, {});
expect(value).toStrictEqual(Buffer.from([12, 34]));
// @ts-expect-error protected
buffalo.position = 1;
const value2 = buffalo.read(BuffaloZclDataType.USE_DATA_TYPE, {length: 1});
expect(value2).toStrictEqual(Buffer.from([34]));
});
it("Writes as buffer when USE_DATA_TYPE is missing dataType option", () => {
const payload = Buffer.from([12, 34]);
const buffer = Buffer.alloc(2);
const buffalo = new BuffaloZcl(buffer);
buffalo.write(BuffaloZclDataType.USE_DATA_TYPE, payload, {});
expect(buffer).toStrictEqual(payload);
});
it("Throws when write USE_DATA_TYPE is missing dataType option and value isnt buffer or number array", () => {
expect(() => {
const payload = "abcd";
const buffalo = new BuffaloZcl(Buffer.alloc(2));
buffalo.write(BuffaloZclDataType.USE_DATA_TYPE, payload, {});
}).toThrow("Cannot write USE_DATA_TYPE without dataType option specified");
});
it("Throws when read GPD_FRAME is missing payload.payloadSize option when payload.commandID is 0xA1", () => {
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(BuffaloZclDataType.GPD_FRAME, {payload: {commandID: 0xa1}});
}).toThrow("Cannot read GPD_FRAME with commandID=0xA1 without payloadSize options specified");
});
it("Throws when read LIST_THERMO_TRANSITIONS is missing required payload options", () => {
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(BuffaloZclDataType.LIST_THERMO_TRANSITIONS, {});
}).toThrow("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(BuffaloZclDataType.LIST_THERMO_TRANSITIONS, {payload: {}});
}).toThrow("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(BuffaloZclDataType.LIST_THERMO_TRANSITIONS, {payload: {mode: 1}});
}).toThrow("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
expect(() => {
const buffalo = new BuffaloZcl(Buffer.alloc(1));
buffalo.read(BuffaloZclDataType.LIST_THERMO_TRANSITIONS, {payload: {numoftrans: 1}});
}).toThrow("Cannot read LIST_THERMO_TRANSITIONS without required payload options specified");
});
});