import {beforeEach, describe, expect, it, test, vi} from "vitest"; 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"; const MANU_SPE_CUSTOM_CLUSTERS = { manuSpecificAssaDoorLock: { name: "manuSpecificAssaDoorLock", ID: 0xfc00, attributes: {}, commands: {}, commandsResponse: {}, }, }; const MIBOXER_GROUPS_CUSTOM_CLUSTER = { genGroups: { name: "genGroups", ID: Zcl.Clusters.genGroups.ID, attributes: {}, commands: { miboxerSetZones: {name: "miboxerSetZones", ID: 0xf0, parameters: [{name: "zones", type: BuffaloZclDataType.LIST_MIBOXER_ZONES}]}, }, commandsResponse: {}, }, }; 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, {}); const cluster2 = Zcl.Utils.getCluster("genBasic", undefined, {}); expect(cluster1).toStrictEqual(cluster2); }); it("Get cluster attribute by ID", () => { const cluster = Zcl.Utils.getCluster(0, undefined, {}); const attribute = Zcl.Utils.getClusterAttribute(cluster, 1, undefined); expect(attribute).toStrictEqual({ID: 1, type: DataType.UINT8, name: "appVersion", default: 0, max: 255}); }); it("Cluster has attribute", () => { const cluster = Zcl.Utils.getCluster(0, undefined, {}); expect(Zcl.Utils.getClusterAttribute(cluster, "zclVersion", undefined)).not.toBeUndefined(); expect(Zcl.Utils.getClusterAttribute(cluster, "NOTEXISTING", undefined)).toBeUndefined(); expect(Zcl.Utils.getClusterAttribute(cluster, 0, undefined)).not.toBeUndefined(); expect(Zcl.Utils.getClusterAttribute(cluster, 910293, undefined)).toBeUndefined(); }); it("Get specific command by name", () => { const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {}); const command = Zcl.Utils.getClusterCommand(cluster, "identify"); expect(command.ID).toBe(0); expect(command.name).toBe("identify"); }); 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("Status 'UNSUP_COMMAND' foundation:nonexisting"); }); it("Get cluster by name non-existing", () => { expect(() => { Zcl.Utils.getCluster("notExisting", undefined, {}); }).toThrow("Status 'UNSUPPORTED_CLUSTER' notExisting"); }); 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 = Zcl.Utils.getClusterCommand(cluster, 0); expect(command).toStrictEqual(Zcl.Utils.getClusterCommand(cluster, "identify")); }); it("Get specific command by name server to client", () => { const cluster = Zcl.Utils.getCluster("genIdentify", undefined, {}); const command = Zcl.Utils.getClusterCommandResponse(cluster, 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, {}); Zcl.Utils.getClusterCommandResponse(cluster, "nonexisting"); }).toThrow("Status 'UNSUP_COMMAND' response genIdentify:nonexisting"); }); it("ZclFrame from buffer parse payload with unknown frame type", () => { expect(() => { // @ts-expect-error 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 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, 0x02, 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: 2, 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]); 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}]; 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, 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: Number.NaN, }; 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, 0x01, 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: 1, }; 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, MANU_SPE_CUSTOM_CLUSTERS, ); 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, MANU_SPE_CUSTOM_CLUSTERS, ); 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, MANU_SPE_CUSTOM_CLUSTERS); 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 genScenes.add", () => { const payload = { groupid: 0, sceneid: 3, scenename: "", transtime: 1, extensionfieldsets: [ {clstId: 6, len: 1, extField: [1]}, {clstId: 768, len: 4, extField: [31260, 27110]}, {clstId: 8, len: 1, extField: [254]}, ], }; const frame = Zcl.Frame.create( FrameType.SPECIFIC, Direction.CLIENT_TO_SERVER, false, undefined, 1, "add", Zcl.Clusters.genScenes.ID, payload, {}, ); expect(frame.toBuffer()).toStrictEqual(Buffer.from([1, 1, 0, 0, 0, 3, 1, 0, 0, 6, 0, 1, 1, 0, 3, 4, 28, 122, 230, 105, 8, 0, 1, 254])); }); 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 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, MANU_SPE_CUSTOM_CLUSTERS); expect(cluster.ID).toBe(0xfc00); expect(cluster.name).toBe("manuSpecificAssaDoorLock"); }); it("Zcl utils get cluster with manufacturerCode", () => { const cluster = Zcl.Utils.getCluster(0xfc57, 0x1217, MANU_SPE_CUSTOM_CLUSTERS); expect(cluster.ID).toBe(0xfc57); expect(cluster.name).toBe("manuSpecificAmazonWWAH"); }); it("Zcl utils get cluster manufacturerCode", () => { const cluster = Zcl.Utils.getCluster(0xfc00, Zcl.ManufacturerCode.UBISYS_TECHNOLOGIES_GMBH, MANU_SPE_CUSTOM_CLUSTERS); expect(cluster.ID).toBe(0xfc00); expect(cluster.name).toBe("manuSpecificAssaDoorLock"); }); it("Zcl utils get cluster manufacturerCode wrong", () => { const cluster = Zcl.Utils.getCluster(0xfc00, 123, MANU_SPE_CUSTOM_CLUSTERS); expect(cluster.ID).toBe(0xfc00); expect(cluster.name).toBe("manuSpecificAssaDoorLock"); }); it("Zcl utils get cluster attributes manufacturerCode", () => { const cluster = Zcl.Utils.getCluster("closuresWindowCovering", 0x1021, { closuresWindowCovering: { ID: Zcl.Clusters.closuresWindowCovering.ID, name: "closuresWindowCovering", attributes: { stepPositionTilt: { name: "stepPositionTilt", ID: 0xf004, type: DataType.ENUM8, manufacturerCode: Zcl.ManufacturerCode.LEGRAND_GROUP, write: true, max: 0xff, }, }, commands: {}, commandsResponse: {}, }, }); const attribute = Zcl.Utils.getClusterAttribute(cluster, 0xf004, 0x1021); expect(attribute).toStrictEqual(expect.objectContaining({ID: 0xf004, manufacturerCode: 0x1021, name: "stepPositionTilt", type: 48})); }); it("Zcl utils get cluster attributes manufacturerCode wrong", () => { const cluster = Zcl.Utils.getCluster("closuresWindowCovering", 123, {}); expect(Zcl.Utils.getClusterAttribute(cluster, 0x1000, 123)).toBeUndefined(); }); it("Zcl utils get command", () => { const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {}); const command = Zcl.Utils.getClusterCommand(cluster, 0); expect(command.name).toStrictEqual("off"); expect(Zcl.Utils.getClusterCommand(cluster, "off")).toStrictEqual(command); }); it("Zcl utils get attribute", () => { const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {}); const attribute = Zcl.Utils.getClusterAttribute(cluster, 16385, undefined); expect(attribute?.name).toStrictEqual("onTime"); expect(Zcl.Utils.getClusterAttribute(cluster, "onTime", undefined)).toStrictEqual(attribute); }); it("Zcl utils get attribute non-existing", () => { const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {}); expect(Zcl.Utils.getClusterAttribute(cluster, "notExisting", undefined)).toBeUndefined(); }); it("Zcl utils get command non-existing", () => { const cluster = Zcl.Utils.getCluster("genOnOff", undefined, {}); expect(() => Zcl.Utils.getClusterCommand(cluster, "notExisting")).toThrow("Status 'UNSUP_COMMAND' genOnOff: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: { payloadSize: buffer.length, 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: { payloadSize: buffer.length, 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: { payloadSize: buffer.length, 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, clusterName: "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, MIBOXER_GROUPS_CUSTOM_CLUSTER, ); 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}, MIBOXER_GROUPS_CUSTOM_CLUSTER, ); 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: Number.NaN, minutes: Number.NaN, seconds: Number.NaN, hundredths: Number.NaN}; 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: Number.NaN, month: Number.NaN, dayOfMonth: Number.NaN, dayOfWeek: Number.NaN}; 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])); buffalo.setPosition(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", () => { expect(() => { const buffalo = new BuffaloZcl(Buffer.alloc(1)); buffalo.read(BuffaloZclDataType.GPD_FRAME, {payload: {commandID: 0xa1}}); }).toThrow("Cannot read GPD_FRAME without required payload 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"); }); });