zigbee-herdsman
Version:
An open source ZigBee gateway solution with node.js.
1,167 lines (1,099 loc) • 75 kB
text/typescript
import * as ZSpec from "../../../src/zspec";
import type {ClusterId, Eui64, ExtendedPanId, NodeId} from "../../../src/zspec/tstypes";
import * as Zcl from "../../../src/zspec/zcl";
import * as Zdo from "../../../src/zspec/zdo";
import {BuffaloZdo} from "../../../src/zspec/zdo/buffaloZdo";
import type {
APSFrameCounterChallengeTLV,
APSFrameCounterResponseTLV,
ActiveEndpointsResponse,
AuthenticationTokenIdTLV,
BeaconAppendixEncapsulationGlobalTLV,
BeaconSurveyConfigurationTLV,
BeaconSurveyResultsTLV,
BindingTableResponse,
ClearAllBindingsReqEUI64TLV,
ConfigurationParametersGlobalTLV,
Curve25519PublicPointTLV,
DeviceAuthenticationLevelTLV,
DeviceCapabilityExtensionGlobalTLV,
DeviceEUI64ListTLV,
EndDeviceAnnounce,
FragmentationParametersGlobalTLV,
IEEEAddressResponse,
JoinerEncapsulationGlobalTLV,
LQITableResponse,
ManufacturerSpecificGlobalTLV,
MatchDescriptorsResponse,
NetworkAddressResponse,
NextChannelChangeGlobalTLV,
NextPanIdChangeGlobalTLV,
NodeDescriptorResponse,
NwkBeaconSurveyResponse,
NwkEnhancedUpdateResponse,
NwkIEEEJoiningListResponse,
NwkUnsolicitedEnhancedUpdateResponse,
NwkUpdateResponse,
PanIdConflictReportGlobalTLV,
PotentialParentsTLV,
PowerDescriptorResponse,
ProcessingStatusTLV,
RouterInformationGlobalTLV,
RoutingTableResponse,
SelectedKeyNegotiationMethodTLV,
ServerMask,
SimpleDescriptorResponse,
SupportedKeyNegotiationMethodsGlobalTLV,
SymmetricPassphraseGlobalTLV,
SystemServerDiscoveryResponse,
TargetIEEEAddressTLV,
} from "../../../src/zspec/zdo/definition/tstypes";
import {uint16To8Array, uint32To8Array} from "../../utils/math";
const IEEE_ADDRESS1: Eui64 = "0xfe34ac2385ff8311";
const IEEE_ADDRESS1_BYTES = [0x11, 0x83, 0xff, 0x85, 0x23, 0xac, 0x34, 0xfe];
const IEEE_ADDRESS2: Eui64 = "0x28373fecd834ba37";
const IEEE_ADDRESS2_BYTES = [0x37, 0xba, 0x34, 0xd8, 0xec, 0x3f, 0x37, 0x28];
const NODE_ID1: NodeId = 0xfe32;
const NODE_ID1_BYTES = uint16To8Array(NODE_ID1);
const NODE_ID2: NodeId = 0xab39;
const NODE_ID2_BYTES = uint16To8Array(NODE_ID2);
const EXT_PAN_ID1: ExtendedPanId = [3, 43, 56, 23, 65, 23, 67, 23];
const EXT_PAN_ID2: ExtendedPanId = [253, 231, 21, 3, 0, 44, 24, 46];
const CLUSTER_LIST1: ClusterId[] = [Zcl.Clusters.genAlarms.ID, Zcl.Clusters.seMetering.ID, Zcl.Clusters.haApplianceStatistics.ID];
const CLUSTER_LIST1_BYTES = [...uint16To8Array(CLUSTER_LIST1[0]), ...uint16To8Array(CLUSTER_LIST1[1]), ...uint16To8Array(CLUSTER_LIST1[2])];
const CLUSTER_LIST2: ClusterId[] = [Zcl.Clusters.genOnOff.ID, Zcl.Clusters.genBasic.ID, Zcl.Clusters.ssIasZone.ID, Zcl.Clusters.genLevelCtrl.ID];
const CLUSTER_LIST2_BYTES = [
...uint16To8Array(CLUSTER_LIST2[0]),
...uint16To8Array(CLUSTER_LIST2[1]),
...uint16To8Array(CLUSTER_LIST2[2]),
...uint16To8Array(CLUSTER_LIST2[3]),
];
const SERVER_MASK_R22: ServerMask = {
primaryTrustCenter: 1,
backupTrustCenter: 1,
deprecated1: 0,
deprecated2: 0,
deprecated3: 0,
deprecated4: 0,
networkManager: 1,
reserved1: 0,
reserved2: 0,
stackComplianceRevision: 22,
};
const SERVER_MASK_R22_BYTE = Zdo.Utils.createServerMask(SERVER_MASK_R22);
const SERVER_MASK_R23: ServerMask = {
primaryTrustCenter: 1,
backupTrustCenter: 1,
deprecated1: 0,
deprecated2: 0,
deprecated3: 0,
deprecated4: 0,
networkManager: 1,
reserved1: 0,
reserved2: 0,
stackComplianceRevision: 23,
};
const SERVER_MASK_R23_BYTE = Zdo.Utils.createServerMask(SERVER_MASK_R23);
describe("ZDO Buffalo", () => {
it("Sets & Gets position", () => {
const buffalo = new BuffaloZdo(Buffer.alloc(3));
expect(buffalo.getPosition()).toStrictEqual(0);
buffalo.setPosition(3);
expect(buffalo.getPosition()).toStrictEqual(3);
buffalo.setPosition(1);
expect(buffalo.getPosition()).toStrictEqual(1);
buffalo.setPosition(0);
expect(buffalo.getPosition()).toStrictEqual(0);
});
it("Sets & Gets bytes without changing internal position", () => {
const buffalo = new BuffaloZdo(Buffer.from([1, 2, 3, 255]));
expect(buffalo.getByte(0)).toStrictEqual(1);
expect(buffalo.getByte(1)).toStrictEqual(2);
expect(buffalo.getByte(2)).toStrictEqual(3);
expect(buffalo.getByte(3)).toStrictEqual(255);
expect(buffalo.getPosition()).toStrictEqual(0);
buffalo.setByte(3, 127);
expect(buffalo.getByte(3)).toStrictEqual(127);
buffalo.setByte(0, 7);
expect(buffalo.getByte(0)).toStrictEqual(7);
});
it("Checks if more available to by amount", () => {
const buffalo = new BuffaloZdo(Buffer.from([1, 2, 3, 255]));
buffalo.setPosition(4);
expect(buffalo.isMoreBy(0)).toBeTruthy();
expect(buffalo.isMoreBy(1)).toBeFalsy();
buffalo.setPosition(1);
expect(buffalo.isMoreBy(0)).toBeTruthy();
expect(buffalo.isMoreBy(3)).toBeTruthy();
expect(buffalo.isMoreBy(4)).toBeFalsy();
});
it("Throws when duplicate TLV tag found and not valid", () => {
expect(() => {
new BuffaloZdo(
Buffer.from([
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
5 - 1,
...uint16To8Array(NODE_ID1),
1,
...uint16To8Array(213),
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
5 - 1,
...uint16To8Array(NODE_ID2),
0,
...uint16To8Array(344),
]),
).readTLVs();
}).toThrow(`Duplicate tag. Cannot have more than one of tagId=${Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS}.`);
expect(() => {
new BuffaloZdo(
Buffer.from([
Zdo.GlobalTLV.MANUFACTURER_SPECIFIC,
2 - 1,
...uint16To8Array(Zcl.ManufacturerCode.ABB),
Zdo.GlobalTLV.MANUFACTURER_SPECIFIC,
2 - 1,
...uint16To8Array(Zcl.ManufacturerCode.ABB_GENWAY_XIAMEN_ELECTRICAL_EQUIPMENT_CO_LTD),
]),
).readTLVs();
}).not.toThrow();
});
it("Throws when encapsulated TLV tag found inside encapsulated", () => {
expect(() => {
new BuffaloZdo(
Buffer.from([Zdo.GlobalTLV.BEACON_APPENDIX_ENCAPSULATION, 4 - 1, Zdo.GlobalTLV.JOINER_ENCAPSULATION, 2 - 1, 123, 456]),
).readTLVs();
}).toThrow(`Invalid nested encapsulation for tagId=${Zdo.GlobalTLV.JOINER_ENCAPSULATION}.`);
});
it("Throws when not enough bytes to read in TLV", () => {
expect(() => {
new BuffaloZdo(Buffer.from([Zdo.GlobalTLV.MANUFACTURER_SPECIFIC, 6 - 1, ...uint16To8Array(Zcl.ManufacturerCode.ABB)])).readTLVs();
}).toThrow(`Malformed TLV. Invalid data length for tagId=${Zdo.GlobalTLV.MANUFACTURER_SPECIFIC}, expected ${6}.`);
});
it("Ignores invalid TLV tag and reads next TLV", () => {
const buffalo = new BuffaloZdo(
Buffer.from([
0xff,
5 - 1,
...uint16To8Array(Zcl.ManufacturerCode.ABB),
4,
5,
6,
Zdo.GlobalTLV.MANUFACTURER_SPECIFIC,
5 - 1,
...uint16To8Array(Zcl.ManufacturerCode.ABB),
1,
2,
3,
]),
);
const tlvs = buffalo.readTLVs();
expect(tlvs).toStrictEqual([
{
tagId: Zdo.GlobalTLV.MANUFACTURER_SPECIFIC,
length: 5,
tlv: {
zigbeeManufacturerId: Zcl.ManufacturerCode.ABB,
additionalData: Buffer.from([1, 2, 3]),
} as ManufacturerSpecificGlobalTLV,
},
]);
});
it("Throws when writing invalid TLV tag", () => {
const buffalo = new BuffaloZdo(Buffer.alloc(3));
expect(() => {
buffalo.writeGlobalTLV({tagId: 0xfe, length: 2, tlv: {nwkPanIdConflictCount: NODE_ID2} as PanIdConflictReportGlobalTLV});
}).toThrow(new Zdo.StatusError(Zdo.Status.NOT_SUPPORTED));
});
it.each([
["readProcessingStatusTLV", {length: 6, error: `Malformed TLV. Invalid length '6', expected 5.`, bytes: [2]}],
["readDeviceAuthenticationLevelTLV", {length: 11, error: `Malformed TLV. Invalid length '11', expected 10.`, bytes: []}],
["readPotentialParentsTLV", {length: 3, error: `Malformed TLV. Invalid length '3', expected at least 4.`, bytes: []}],
["readPotentialParentsTLV", {length: 11, error: `Malformed TLV. Invalid length '11', expected 13.`, bytes: [1, 2, 230, 3]}],
["readBeaconSurveyResultsTLV", {length: 6, error: `Malformed TLV. Invalid length '6', expected 4.`, bytes: []}],
["readAPSFrameCounterResponseTLV", {length: 36, error: `Malformed TLV. Invalid length '36', expected 32.`, bytes: []}],
// ['readDeviceEUI64ListTLV', {length: 36, error: `Malformed TLV. Invalid length '36', expected 41.`, bytes: [5]}],
// ['readSelectedKeyNegotiationMethodTLV', {length: 11, error: `Malformed TLV. Invalid length '11', expected 10.`, bytes: []}],
// ['readTargetIEEEAddressTLV', {length: 11, error: `Malformed TLV. Invalid length '11', expected 8.`, bytes: []}],
["readCurve25519PublicPointTLV", {length: 11, error: `Malformed TLV. Invalid length '11', expected 40.`, bytes: []}],
// ['readBeaconSurveyConfigurationTLV', {length: 3, error: `Malformed TLV. Invalid length '3', expected 6.`, bytes: [1]}],
["readConfigurationParametersGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readBeaconAppendixEncapsulationGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readJoinerEncapsulationGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readFragmentationParametersGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readRouterInformationGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readSymmetricPassphraseGlobalTLV", {length: 13, error: `Malformed TLV. Invalid length '13', expected at least 16.`, bytes: []}],
["readNextChannelChangeGlobalTLV", {length: 3, error: `Malformed TLV. Invalid length '3', expected at least 4.`, bytes: []}],
["readNextPanIdChangeGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readPanIdConflictReportGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readSupportedKeyNegotiationMethodsGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
["readManufacturerSpecificGlobalTLV", {length: 1, error: `Malformed TLV. Invalid length '1', expected at least 2.`, bytes: []}],
])("Throws when reading invalid length TLV %s", (func, payload) => {
expect(() => {
const buffalo = new BuffaloZdo(Buffer.from(payload.bytes));
buffalo[func](payload.length);
}).toThrow(payload.error);
});
it.each([
[
"MANUFACTURER_SPECIFIC",
[Zdo.GlobalTLV.MANUFACTURER_SPECIFIC, 3 - 1, ...uint16To8Array(256), 123],
[
{
tagId: Zdo.GlobalTLV.MANUFACTURER_SPECIFIC,
length: 3,
tlv: {zigbeeManufacturerId: 256, additionalData: Buffer.from([123])} as ManufacturerSpecificGlobalTLV,
},
],
],
[
"SUPPORTED_KEY_NEGOTIATION_METHODS",
[Zdo.GlobalTLV.SUPPORTED_KEY_NEGOTIATION_METHODS, 2 - 1, 1, 2],
[
{
tagId: Zdo.GlobalTLV.SUPPORTED_KEY_NEGOTIATION_METHODS,
length: 2,
tlv: {
keyNegotiationProtocolsBitmask: 1,
preSharedSecretsBitmask: 2,
sourceDeviceEui64: undefined,
} as SupportedKeyNegotiationMethodsGlobalTLV,
},
],
],
[
"SUPPORTED_KEY_NEGOTIATION_METHODS with IEEE",
[Zdo.GlobalTLV.SUPPORTED_KEY_NEGOTIATION_METHODS, 10 - 1, 1, 2, ...IEEE_ADDRESS1_BYTES],
[
{
tagId: Zdo.GlobalTLV.SUPPORTED_KEY_NEGOTIATION_METHODS,
length: 10,
tlv: {
keyNegotiationProtocolsBitmask: 1,
preSharedSecretsBitmask: 2,
sourceDeviceEui64: IEEE_ADDRESS1,
} as SupportedKeyNegotiationMethodsGlobalTLV,
},
],
],
[
"PAN_ID_CONFLICT_REPORT",
[Zdo.GlobalTLV.PAN_ID_CONFLICT_REPORT, 2 - 1, ...NODE_ID2_BYTES],
[{tagId: Zdo.GlobalTLV.PAN_ID_CONFLICT_REPORT, length: 2, tlv: {nwkPanIdConflictCount: NODE_ID2} as PanIdConflictReportGlobalTLV}],
],
[
"NEXT_PAN_ID_CHANGE",
[Zdo.GlobalTLV.NEXT_PAN_ID_CHANGE, 2 - 1, ...uint16To8Array(0xff00)],
[{tagId: Zdo.GlobalTLV.NEXT_PAN_ID_CHANGE, length: 2, tlv: {panId: 0xff00} as NextPanIdChangeGlobalTLV}],
],
[
"NEXT_CHANNEL_CHANGE",
[Zdo.GlobalTLV.NEXT_CHANNEL_CHANGE, 4 - 1, ...uint32To8Array(423432)],
[{tagId: Zdo.GlobalTLV.NEXT_CHANNEL_CHANGE, length: 4, tlv: {channel: 423432} as NextChannelChangeGlobalTLV}],
],
[
"SYMMETRIC_PASSPHRASE",
[
Zdo.GlobalTLV.SYMMETRIC_PASSPHRASE,
ZSpec.DEFAULT_ENCRYPTION_KEY_SIZE - 1,
...Buffer.alloc(ZSpec.DEFAULT_ENCRYPTION_KEY_SIZE).fill(0xca),
],
[
{
tagId: Zdo.GlobalTLV.SYMMETRIC_PASSPHRASE,
length: ZSpec.DEFAULT_ENCRYPTION_KEY_SIZE,
tlv: {passphrase: Buffer.alloc(ZSpec.DEFAULT_ENCRYPTION_KEY_SIZE).fill(0xca)} as SymmetricPassphraseGlobalTLV,
},
],
],
[
"ROUTER_INFORMATION",
[Zdo.GlobalTLV.ROUTER_INFORMATION, 2 - 1, ...uint16To8Array(4396)],
[{tagId: Zdo.GlobalTLV.ROUTER_INFORMATION, length: 2, tlv: {bitmask: 4396} as RouterInformationGlobalTLV}],
],
[
"FRAGMENTATION_PARAMETERS",
[Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS, 5 - 1, ...NODE_ID1_BYTES, 5, ...uint16To8Array(32456)],
[
{
tagId: Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
length: 5,
tlv: {nwkAddress: NODE_ID1, fragmentationOptions: 5, maxIncomingTransferUnit: 32456} as FragmentationParametersGlobalTLV,
},
],
],
[
"JOINER_ENCAPSULATION",
[Zdo.GlobalTLV.JOINER_ENCAPSULATION, 4 - 1, Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS, 2 - 1, ...NODE_ID1_BYTES],
[
{
tagId: Zdo.GlobalTLV.JOINER_ENCAPSULATION,
length: 4,
tlv: {
additionalTLVs: [
{
tagId: Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
length: 2,
tlv: {nwkAddress: NODE_ID1, fragmentationOptions: undefined, maxIncomingTransferUnit: undefined},
},
],
} as JoinerEncapsulationGlobalTLV,
},
],
],
[
"BEACON_APPENDIX_ENCAPSULATION",
[Zdo.GlobalTLV.BEACON_APPENDIX_ENCAPSULATION, 4 - 1, Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS, 2 - 1, ...NODE_ID1_BYTES],
[
{
tagId: Zdo.GlobalTLV.BEACON_APPENDIX_ENCAPSULATION,
length: 4,
tlv: {
additionalTLVs: [
{
tagId: Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
length: 2,
tlv: {nwkAddress: NODE_ID1, fragmentationOptions: undefined, maxIncomingTransferUnit: undefined},
},
],
} as BeaconAppendixEncapsulationGlobalTLV,
},
],
],
[
"CONFIGURATION_PARAMETERS",
[Zdo.GlobalTLV.CONFIGURATION_PARAMETERS, 2 - 1, ...uint16To8Array(47593)],
[{tagId: Zdo.GlobalTLV.CONFIGURATION_PARAMETERS, length: 2, tlv: {configurationParameters: 47593} as ConfigurationParametersGlobalTLV}],
],
[
"DEVICE_CAPABILITY_EXTENSION",
[Zdo.GlobalTLV.DEVICE_CAPABILITY_EXTENSION, 3 - 1, 3, 1, 2],
[
{
tagId: Zdo.GlobalTLV.DEVICE_CAPABILITY_EXTENSION,
length: 3,
tlv: {data: Buffer.from([3, 1, 2])} as DeviceCapabilityExtensionGlobalTLV,
},
],
],
["invalid", [0xfe, 0, 0], []],
])("Reads & Writes global TLV %s", (_name, bytes, expected) => {
const readBuffalo = new BuffaloZdo(Buffer.from(bytes));
expect(readBuffalo.readTLVs()).toStrictEqual(expected);
const writeBuffer = new BuffaloZdo(Buffer.alloc(255));
writeBuffer.writeGlobalTLVs(expected);
expect(writeBuffer.getWritten()).toStrictEqual(Buffer.from(expected.length ? bytes : []));
});
it("Throws when building unknown cluster ID", () => {
const clusterId = 0x8005;
expect(() => {
// @ts-expect-error
BuffaloZdo.buildRequest(true, clusterId, Buffer.from([1, 123]));
}).toThrow(`Unsupported request building for cluster ID '${clusterId}'.`);
});
it("buildNetworkAddressRequest without ZDO_MESSAGE_OVERHEAD", () => {
expect(Zdo.Buffalo.buildRequest(false, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, IEEE_ADDRESS1, false, 1)).toStrictEqual(
Buffer.from([...IEEE_ADDRESS1_BYTES, 0, 1]),
);
expect(Zdo.Buffalo.buildRequest(false, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, IEEE_ADDRESS2, true, 3)).toStrictEqual(
Buffer.from([...IEEE_ADDRESS2_BYTES, 1, 3]),
);
});
it("buildNetworkAddressRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, IEEE_ADDRESS1, false, 1)).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS1_BYTES, 0, 1]),
);
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NETWORK_ADDRESS_REQUEST, IEEE_ADDRESS2, true, 3)).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS2_BYTES, 1, 3]),
);
});
it("buildIeeeAddressRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, NODE_ID1, false, 1)).toStrictEqual(
Buffer.from([0, ...NODE_ID1_BYTES, 0, 1]),
);
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.IEEE_ADDRESS_REQUEST, NODE_ID1, true, 3)).toStrictEqual(
Buffer.from([0, ...NODE_ID1_BYTES, 1, 3]),
);
});
it("buildNodeDescriptorRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, NODE_ID1)).toStrictEqual(Buffer.from([0, ...NODE_ID1_BYTES]));
const tlv: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
/*fragmentationOptions: undefined,*/
/*maxIncomingTransferUnit: undefined,*/
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, NODE_ID1, tlv)).toStrictEqual(
Buffer.from([0, ...NODE_ID1_BYTES, Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS, 1, ...uint16To8Array(tlv.nwkAddress)]),
);
const tlv2: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
fragmentationOptions: 1,
/*maxIncomingTransferUnit: undefined,*/
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, NODE_ID1, tlv2)).toStrictEqual(
Buffer.from([
0,
...NODE_ID1_BYTES,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
2,
...uint16To8Array(tlv2.nwkAddress),
tlv2.fragmentationOptions!,
]),
);
const tlv3: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
/*fragmentationOptions: undefined,*/
maxIncomingTransferUnit: 256,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, NODE_ID1, tlv3)).toStrictEqual(
Buffer.from([
0,
...NODE_ID1_BYTES,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
3,
...uint16To8Array(tlv3.nwkAddress),
...uint16To8Array(tlv3.maxIncomingTransferUnit!),
]),
);
const tlv4: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
fragmentationOptions: 1,
maxIncomingTransferUnit: 65352,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NODE_DESCRIPTOR_REQUEST, NODE_ID1, tlv4)).toStrictEqual(
Buffer.from([
0,
...NODE_ID1_BYTES,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
4,
...uint16To8Array(tlv4.nwkAddress),
tlv4.fragmentationOptions!,
...uint16To8Array(tlv4.maxIncomingTransferUnit!),
]),
);
});
it("buildPowerDescriptorRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.POWER_DESCRIPTOR_REQUEST, NODE_ID1)).toStrictEqual(Buffer.from([0, ...NODE_ID1_BYTES]));
});
it("buildSimpleDescriptorRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.SIMPLE_DESCRIPTOR_REQUEST, NODE_ID1, 3)).toStrictEqual(
Buffer.from([0, ...NODE_ID1_BYTES, 3]),
);
});
it("buildActiveEndpointsRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.ACTIVE_ENDPOINTS_REQUEST, NODE_ID1)).toStrictEqual(Buffer.from([0, ...NODE_ID1_BYTES]));
});
it("buildMatchDescriptorRequest", () => {
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.MATCH_DESCRIPTORS_REQUEST, NODE_ID1, ZSpec.HA_PROFILE_ID, CLUSTER_LIST1, CLUSTER_LIST2),
).toStrictEqual(
Buffer.from([
0,
...NODE_ID1_BYTES,
...uint16To8Array(ZSpec.HA_PROFILE_ID),
CLUSTER_LIST1.length,
...CLUSTER_LIST1_BYTES,
CLUSTER_LIST2.length,
...CLUSTER_LIST2_BYTES,
]),
);
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.MATCH_DESCRIPTORS_REQUEST, NODE_ID1, ZSpec.HA_PROFILE_ID, CLUSTER_LIST1, []),
).toStrictEqual(Buffer.from([0, ...NODE_ID1_BYTES, ...uint16To8Array(ZSpec.HA_PROFILE_ID), CLUSTER_LIST1.length, ...CLUSTER_LIST1_BYTES, 0]));
});
it("buildSystemServiceDiscoveryRequest", () => {
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.SYSTEM_SERVER_DISCOVERY_REQUEST, {
primaryTrustCenter: 1,
backupTrustCenter: 0,
deprecated1: 0,
deprecated2: 0,
deprecated3: 0,
deprecated4: 0,
networkManager: 0,
reserved1: 0,
reserved2: 0,
stackComplianceRevision: 0,
}),
).toStrictEqual(Buffer.from([0, ...uint16To8Array(0b0000000000000001)]));
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.SYSTEM_SERVER_DISCOVERY_REQUEST, {
primaryTrustCenter: 1,
backupTrustCenter: 0,
deprecated1: 0,
deprecated2: 0,
deprecated3: 0,
deprecated4: 0,
networkManager: 1,
reserved1: 0,
reserved2: 0,
stackComplianceRevision: 23,
}),
).toStrictEqual(Buffer.from([0, ...uint16To8Array(0b0010111001000001)]));
});
it("buildParentAnnounce", () => {
const children = [IEEE_ADDRESS1, IEEE_ADDRESS2];
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.PARENT_ANNOUNCE, children)).toStrictEqual(
Buffer.from([0, children.length, ...IEEE_ADDRESS1_BYTES, ...IEEE_ADDRESS2_BYTES]),
);
});
it("buildBindRequest", () => {
expect(
Zdo.Buffalo.buildRequest(
true,
Zdo.ClusterId.BIND_REQUEST,
IEEE_ADDRESS1,
2,
Zcl.Clusters.seMetering.ID,
Zdo.UNICAST_BINDING,
IEEE_ADDRESS2,
123,
64,
),
).toStrictEqual(
Buffer.from([
0,
...IEEE_ADDRESS1_BYTES,
2,
...uint16To8Array(Zcl.Clusters.seMetering.ID),
Zdo.UNICAST_BINDING,
...IEEE_ADDRESS2_BYTES,
64,
]),
);
expect(
Zdo.Buffalo.buildRequest(
true,
Zdo.ClusterId.BIND_REQUEST,
IEEE_ADDRESS1,
3,
Zcl.Clusters.seMetering.ID,
Zdo.MULTICAST_BINDING,
IEEE_ADDRESS2,
123,
64,
),
).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS1_BYTES, 3, ...uint16To8Array(Zcl.Clusters.seMetering.ID), Zdo.MULTICAST_BINDING, ...uint16To8Array(123)]),
);
});
it("buildUnbindRequest", () => {
expect(
Zdo.Buffalo.buildRequest(
true,
Zdo.ClusterId.UNBIND_REQUEST,
IEEE_ADDRESS1,
2,
Zcl.Clusters.seMetering.ID,
Zdo.UNICAST_BINDING,
IEEE_ADDRESS2,
123,
64,
),
).toStrictEqual(
Buffer.from([
0,
...IEEE_ADDRESS1_BYTES,
2,
...uint16To8Array(Zcl.Clusters.seMetering.ID),
Zdo.UNICAST_BINDING,
...IEEE_ADDRESS2_BYTES,
64,
]),
);
expect(
Zdo.Buffalo.buildRequest(
true,
Zdo.ClusterId.UNBIND_REQUEST,
IEEE_ADDRESS1,
3,
Zcl.Clusters.seMetering.ID,
Zdo.MULTICAST_BINDING,
IEEE_ADDRESS2,
123,
64,
),
).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS1_BYTES, 3, ...uint16To8Array(Zcl.Clusters.seMetering.ID), Zdo.MULTICAST_BINDING, ...uint16To8Array(123)]),
);
});
it("Throws when buildBindRequest/buildUnbindRequest invalid type", () => {
expect(() => {
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.BIND_REQUEST, IEEE_ADDRESS1, 2, Zcl.Clusters.seMetering.ID, 99, IEEE_ADDRESS2, 123, 64);
}).toThrow(`Status 'NOT_SUPPORTED'`);
expect(() => {
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.UNBIND_REQUEST, IEEE_ADDRESS1, 2, Zcl.Clusters.seMetering.ID, 99, IEEE_ADDRESS2, 123, 64);
}).toThrow(`Status 'NOT_SUPPORTED'`);
});
it("buildClearAllBindingsRequest", () => {
const eui64List = [IEEE_ADDRESS1, IEEE_ADDRESS2];
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.CLEAR_ALL_BINDINGS_REQUEST, {eui64List} as ClearAllBindingsReqEUI64TLV)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE * eui64List.length + 1 - 1, eui64List.length, ...IEEE_ADDRESS1_BYTES, ...IEEE_ADDRESS2_BYTES]),
);
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.CLEAR_ALL_BINDINGS_REQUEST, {eui64List: []} as ClearAllBindingsReqEUI64TLV),
).toStrictEqual(Buffer.from([0, 0, 0, 0]));
});
it("buildLqiTableRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.LQI_TABLE_REQUEST, 1)).toStrictEqual(Buffer.from([0, 1]));
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.LQI_TABLE_REQUEST, 254)).toStrictEqual(Buffer.from([0, 254]));
});
it("buildRoutingTableRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.ROUTING_TABLE_REQUEST, 1)).toStrictEqual(Buffer.from([0, 1]));
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.ROUTING_TABLE_REQUEST, 254)).toStrictEqual(Buffer.from([0, 254]));
});
it("buildBindingTableRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.BINDING_TABLE_REQUEST, 1)).toStrictEqual(Buffer.from([0, 1]));
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.BINDING_TABLE_REQUEST, 254)).toStrictEqual(Buffer.from([0, 254]));
});
it("buildLeaveRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.LEAVE_REQUEST, IEEE_ADDRESS2, Zdo.LeaveRequestFlags.WITHOUT_REJOIN)).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS2_BYTES, Zdo.LeaveRequestFlags.WITHOUT_REJOIN]),
);
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.LEAVE_REQUEST, IEEE_ADDRESS2, Zdo.LeaveRequestFlags.AND_REJOIN)).toStrictEqual(
Buffer.from([0, ...IEEE_ADDRESS2_BYTES, Zdo.LeaveRequestFlags.AND_REJOIN]),
);
});
it("buildPermitJoining", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.PERMIT_JOINING_REQUEST, 254, 1, [])).toStrictEqual(Buffer.from([0, 254, 1]));
const tlvs = [
{
tagId: Zdo.GlobalTLV.BEACON_APPENDIX_ENCAPSULATION,
length: 4,
tlv: {
additionalTLVs: [{tagId: Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS, length: 2, tlv: {nwkAddress: NODE_ID1}}],
},
},
];
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.PERMIT_JOINING_REQUEST, 255, 1, tlvs)).toStrictEqual(
Buffer.from([
0,
255,
1,
Zdo.GlobalTLV.BEACON_APPENDIX_ENCAPSULATION,
4 - 1,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
2 - 1,
...NODE_ID1_BYTES,
]),
);
});
it("buildScanChannelsRequest", () => {
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_UPDATE_REQUEST, ZSpec.ALL_802_15_4_CHANNELS, 3, 3, undefined, undefined),
).toStrictEqual(Buffer.from([0, ...uint32To8Array(ZSpec.ALL_802_15_4_CHANNELS_MASK), 3, 3]));
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_UPDATE_REQUEST, ZSpec.ALL_802_15_4_CHANNELS, 64, 3, undefined, undefined),
).toStrictEqual(Buffer.from([0, ...uint32To8Array(ZSpec.ALL_802_15_4_CHANNELS_MASK), 64 /*, 3*/]));
});
it("buildChannelChangeRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_UPDATE_REQUEST, [15], 0xfe, undefined, 1, undefined)).toStrictEqual(
Buffer.from([0, ...uint32To8Array(ZSpec.Utils.channelsToUInt32Mask([15])), 0xfe, 1]),
);
});
it("buildSetActiveChannelsAndNwkManagerIdRequest", () => {
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_UPDATE_REQUEST, ZSpec.PREFERRED_802_15_4_CHANNELS, 0xff, undefined, 3, 123),
).toStrictEqual(Buffer.from([0, ...uint32To8Array(ZSpec.PREFERRED_802_15_4_CHANNELS_MASK), 0xff, 3, ...uint16To8Array(123)]));
});
it("buildEnhancedScanChannelsRequest", () => {
const channelPages = [123, 54394, 29344];
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_ENHANCED_UPDATE_REQUEST, channelPages, 5, 3, undefined, undefined, 1)).toStrictEqual(
Buffer.from([
0,
channelPages.length,
...uint32To8Array(channelPages[0]),
...uint32To8Array(channelPages[1]),
...uint32To8Array(channelPages[2]),
5,
3,
1,
]),
);
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_ENHANCED_UPDATE_REQUEST, channelPages, 6, 3, undefined, undefined, 1)).toStrictEqual(
Buffer.from([
0,
channelPages.length,
...uint32To8Array(channelPages[0]),
...uint32To8Array(channelPages[1]),
...uint32To8Array(channelPages[2]),
6 /*, 3*/,
1,
]),
);
});
it("buildEnhancedChannelChangeRequest", () => {
const channelPage = 54394;
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_ENHANCED_UPDATE_REQUEST, [channelPage], 0xfe, undefined, 3, undefined, 1),
).toStrictEqual(Buffer.from([0, 1, ...uint32To8Array(channelPage), 0xfe, 3, 1]));
});
it("buildEnhancedSetActiveChannelsAndNwkManagerIdRequest", () => {
const channelPages = [123, 54394, 29344];
const nwkManagerAddr = 0xfe01;
expect(
Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_ENHANCED_UPDATE_REQUEST, channelPages, 0xff, undefined, 2, nwkManagerAddr, 1),
).toStrictEqual(
Buffer.from([
0,
channelPages.length,
...uint32To8Array(channelPages[0]),
...uint32To8Array(channelPages[1]),
...uint32To8Array(channelPages[2]),
0xff,
2,
...uint16To8Array(nwkManagerAddr),
1,
]),
);
});
it("buildNwkIEEEJoiningListRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_IEEE_JOINING_LIST_REQUEST, 3)).toStrictEqual(Buffer.from([0, 3]));
});
it("buildNwkBeaconSurveyRequest", () => {
const tlv: BeaconSurveyConfigurationTLV = {
scanChannelList: [],
configurationBitmask: 0,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_BEACON_SURVEY_REQUEST, tlv)).toStrictEqual(Buffer.from([0, 0, 2 - 1, 0, 0]));
const tlv2: BeaconSurveyConfigurationTLV = {
scanChannelList: [34252],
configurationBitmask: 1,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_BEACON_SURVEY_REQUEST, tlv2)).toStrictEqual(
Buffer.from([0, 0, 6 - 1, 1, ...uint32To8Array(tlv2.scanChannelList[0]), 1]),
);
const tlv3: BeaconSurveyConfigurationTLV = {
scanChannelList: [34252, 123],
configurationBitmask: 1,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.NWK_BEACON_SURVEY_REQUEST, tlv3)).toStrictEqual(
Buffer.from([0, 0, 10 - 1, 2, ...uint32To8Array(tlv3.scanChannelList[0]), ...uint32To8Array(tlv3.scanChannelList[1]), 1]),
);
});
it("buildStartKeyNegotiationRequest", () => {
const tlv: Curve25519PublicPointTLV = {
eui64: IEEE_ADDRESS1,
publicPoint: Buffer.alloc(Zdo.CURVE_PUBLIC_POINT_SIZE).fill(0xcd),
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.START_KEY_NEGOTIATION_REQUEST, tlv)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE + Zdo.CURVE_PUBLIC_POINT_SIZE - 1, ...IEEE_ADDRESS1_BYTES, ...tlv.publicPoint]),
);
const tlv2: Curve25519PublicPointTLV = {
eui64: IEEE_ADDRESS2,
publicPoint: Buffer.alloc(Zdo.CURVE_PUBLIC_POINT_SIZE).fill(0x3c),
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.START_KEY_NEGOTIATION_REQUEST, tlv2)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE + Zdo.CURVE_PUBLIC_POINT_SIZE - 1, ...IEEE_ADDRESS2_BYTES, ...tlv2.publicPoint]),
);
});
it("buildRetrieveAuthenticationTokenRequest", () => {
const tlv: AuthenticationTokenIdTLV = {tlvTypeTagId: 0};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.RETRIEVE_AUTHENTICATION_TOKEN_REQUEST, tlv)).toStrictEqual(Buffer.from([0, 0, 1 - 1, 0]));
const tlv2: AuthenticationTokenIdTLV = {tlvTypeTagId: 31};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.RETRIEVE_AUTHENTICATION_TOKEN_REQUEST, tlv2)).toStrictEqual(
Buffer.from([0, 0, 1 - 1, 31]),
);
});
it("buildGetAuthenticationLevelRequest", () => {
const tlv: TargetIEEEAddressTLV = {ieee: IEEE_ADDRESS2};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.GET_AUTHENTICATION_LEVEL_REQUEST, tlv)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE - 1, ...IEEE_ADDRESS2_BYTES]),
);
});
it.each([
{panId: 0xfeef, channel: 0, configurationParameters: 0},
{panId: 0x1234, channel: 15, configurationParameters: 0},
{panId: 0x1234, channel: 0, configurationParameters: 1},
{panId: 0x6543, channel: 45, configurationParameters: 1},
])("buildSetConfigurationRequest", ({panId, channel, configurationParameters}) => {
expect(
Zdo.Buffalo.buildRequest(
true,
Zdo.ClusterId.SET_CONFIGURATION_REQUEST,
{panId} as NextPanIdChangeGlobalTLV,
{channel} as NextChannelChangeGlobalTLV,
{configurationParameters} as ConfigurationParametersGlobalTLV,
),
).toStrictEqual(
Buffer.from([
0,
Zdo.GlobalTLV.NEXT_PAN_ID_CHANGE,
ZSpec.PAN_ID_SIZE - 1,
...uint16To8Array(panId),
Zdo.GlobalTLV.NEXT_CHANNEL_CHANGE,
4 - 1,
...uint32To8Array(channel),
Zdo.GlobalTLV.CONFIGURATION_PARAMETERS,
2 - 1,
...uint16To8Array(configurationParameters),
]),
);
});
it("buildGetConfigurationRequest", () => {
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.GET_CONFIGURATION_REQUEST, [84])).toStrictEqual(Buffer.from([0, 1, 84]));
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.GET_CONFIGURATION_REQUEST, [67, 71])).toStrictEqual(Buffer.from([0, 2, 67, 71]));
});
it("buildStartKeyUpdateRequest", () => {
const method: SelectedKeyNegotiationMethodTLV = {
protocol: Zdo.SelectedKeyNegotiationProtocol.SPEKE_CURVE25519_SHA256,
presharedSecret: Zdo.SelectedPreSharedSecret.BASIC_AUTHORIZATION_KEY,
sendingDeviceEui64: IEEE_ADDRESS2,
};
const params: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
fragmentationOptions: 1,
maxIncomingTransferUnit: 2345,
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.START_KEY_UPDATE_REQUEST, method, params)).toStrictEqual(
Buffer.from([
0,
0,
ZSpec.EUI64_SIZE + 2 - 1,
method.protocol,
method.presharedSecret,
...IEEE_ADDRESS2_BYTES,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
5 - 1,
...NODE_ID1_BYTES,
params.fragmentationOptions!,
...uint16To8Array(params.maxIncomingTransferUnit!),
]),
);
});
it("buildDecommissionRequest", () => {
const tlv: DeviceEUI64ListTLV = {
eui64List: [IEEE_ADDRESS1],
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.DECOMMISSION_REQUEST, tlv)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE * tlv.eui64List.length + 1 - 1, tlv.eui64List.length, ...IEEE_ADDRESS1_BYTES]),
);
const tlv2: DeviceEUI64ListTLV = {
eui64List: [IEEE_ADDRESS2, IEEE_ADDRESS1],
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.DECOMMISSION_REQUEST, tlv2)).toStrictEqual(
Buffer.from([
0,
0,
ZSpec.EUI64_SIZE * tlv2.eui64List.length + 1 - 1,
tlv2.eui64List.length,
...IEEE_ADDRESS2_BYTES,
...IEEE_ADDRESS1_BYTES,
]),
);
const tlv3: DeviceEUI64ListTLV = {
eui64List: [],
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.DECOMMISSION_REQUEST, tlv3)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE * tlv3.eui64List.length + 1 - 1, tlv3.eui64List.length]),
);
});
it("buildChallengeRequest", () => {
const tlv: APSFrameCounterChallengeTLV = {
senderEui64: IEEE_ADDRESS2,
challengeValue: Buffer.alloc(Zdo.CHALLENGE_VALUE_SIZE).fill(0xfe),
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.CHALLENGE_REQUEST, tlv)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE + Zdo.CHALLENGE_VALUE_SIZE - 1, ...IEEE_ADDRESS2_BYTES, ...tlv.challengeValue]),
);
const tlv2: APSFrameCounterChallengeTLV = {
senderEui64: IEEE_ADDRESS1,
challengeValue: Buffer.from([0xfe, 0xac, 0x12, 0x23, 0x85, 0x8c, 0x7c, 0xa3]),
};
expect(Zdo.Buffalo.buildRequest(true, Zdo.ClusterId.CHALLENGE_REQUEST, tlv2)).toStrictEqual(
Buffer.from([0, 0, ZSpec.EUI64_SIZE + Zdo.CHALLENGE_VALUE_SIZE - 1, ...IEEE_ADDRESS1_BYTES, ...tlv2.challengeValue]),
);
});
it.each([
Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE,
Zdo.ClusterId.IEEE_ADDRESS_RESPONSE,
Zdo.ClusterId.NODE_DESCRIPTOR_RESPONSE,
Zdo.ClusterId.POWER_DESCRIPTOR_RESPONSE,
Zdo.ClusterId.SIMPLE_DESCRIPTOR_RESPONSE,
Zdo.ClusterId.ACTIVE_ENDPOINTS_RESPONSE,
Zdo.ClusterId.MATCH_DESCRIPTORS_RESPONSE,
Zdo.ClusterId.SYSTEM_SERVER_DISCOVERY_RESPONSE,
Zdo.ClusterId.PARENT_ANNOUNCE_RESPONSE,
Zdo.ClusterId.BIND_RESPONSE,
Zdo.ClusterId.UNBIND_RESPONSE,
Zdo.ClusterId.CLEAR_ALL_BINDINGS_RESPONSE,
Zdo.ClusterId.LQI_TABLE_RESPONSE,
Zdo.ClusterId.ROUTING_TABLE_RESPONSE,
Zdo.ClusterId.BINDING_TABLE_RESPONSE,
Zdo.ClusterId.LEAVE_RESPONSE,
Zdo.ClusterId.PERMIT_JOINING_RESPONSE,
Zdo.ClusterId.NWK_UPDATE_RESPONSE,
Zdo.ClusterId.NWK_ENHANCED_UPDATE_RESPONSE,
Zdo.ClusterId.NWK_IEEE_JOINING_LIST_RESPONSE,
Zdo.ClusterId.NWK_UNSOLICITED_ENHANCED_UPDATE_RESPONSE,
Zdo.ClusterId.NWK_BEACON_SURVEY_RESPONSE,
Zdo.ClusterId.START_KEY_NEGOTIATION_RESPONSE,
Zdo.ClusterId.RETRIEVE_AUTHENTICATION_TOKEN_RESPONSE,
Zdo.ClusterId.GET_AUTHENTICATION_LEVEL_RESPONSE,
Zdo.ClusterId.SET_CONFIGURATION_RESPONSE,
Zdo.ClusterId.GET_CONFIGURATION_RESPONSE,
Zdo.ClusterId.START_KEY_UPDATE_RESPONSE,
Zdo.ClusterId.DECOMMISSION_RESPONSE,
Zdo.ClusterId.CHALLENGE_RESPONSE,
])("Returns status and undefined when reading unsuccessful response for %s", (clusterId) => {
const buffer = Buffer.from([1, Zdo.Status.INV_REQUESTTYPE, 1, 2, 3]);
expect(Zdo.Buffalo.readResponse(true, clusterId, buffer)).toStrictEqual([Zdo.Status.INV_REQUESTTYPE, undefined]);
});
it("Throws when reading unknown cluster ID", () => {
const clusterId = 0x0005;
expect(() => {
BuffaloZdo.readResponse(true, clusterId, Buffer.from([1, 123]));
}).toThrow(`Unsupported response reading for cluster ID '${clusterId}'.`);
});
it("readNetworkAddressResponse", () => {
const buffer = Buffer.from([1, Zdo.Status.SUCCESS, ...IEEE_ADDRESS1_BYTES, ...NODE_ID1_BYTES]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE, buffer)).toStrictEqual([
Zdo.Status.SUCCESS,
{
eui64: IEEE_ADDRESS1,
nwkAddress: NODE_ID1,
startIndex: 0,
assocDevList: [],
} as NetworkAddressResponse,
]);
const bufferWAssoc = Buffer.from([
1,
Zdo.Status.SUCCESS,
...IEEE_ADDRESS2_BYTES,
...NODE_ID1_BYTES,
2,
3,
...uint16To8Array(123),
...uint16To8Array(52523),
]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.NETWORK_ADDRESS_RESPONSE, bufferWAssoc)).toStrictEqual([
Zdo.Status.SUCCESS,
{
eui64: IEEE_ADDRESS2,
nwkAddress: NODE_ID1,
startIndex: 3,
assocDevList: [123, 52523],
} as NetworkAddressResponse,
]);
});
it("readIEEEAddressResponse", () => {
const buffer = Buffer.from([1, Zdo.Status.SUCCESS, ...IEEE_ADDRESS1_BYTES, ...NODE_ID1_BYTES]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.IEEE_ADDRESS_RESPONSE, buffer)).toStrictEqual([
Zdo.Status.SUCCESS,
{
eui64: IEEE_ADDRESS1,
nwkAddress: NODE_ID1,
startIndex: 0,
assocDevList: [],
} as IEEEAddressResponse,
]);
const bufferWAssoc = Buffer.from([
1,
Zdo.Status.SUCCESS,
...IEEE_ADDRESS2_BYTES,
...NODE_ID1_BYTES,
2,
3,
...uint16To8Array(123),
...uint16To8Array(52523),
]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.IEEE_ADDRESS_RESPONSE, bufferWAssoc)).toStrictEqual([
Zdo.Status.SUCCESS,
{
eui64: IEEE_ADDRESS2,
nwkAddress: NODE_ID1,
startIndex: 3,
assocDevList: [123, 52523],
} as IEEEAddressResponse,
]);
});
it("readNodeDescriptorResponse", () => {
const buffer = Buffer.from([
1,
Zdo.Status.SUCCESS,
...NODE_ID1_BYTES,
0b00100010,
0b00100000,
0b00001110,
...uint16To8Array(Zcl.ManufacturerCode.BARACODA_SA),
0x7c,
...uint16To8Array(0x7eff),
...uint16To8Array(SERVER_MASK_R22_BYTE),
...uint16To8Array(0x3cff),
0,
]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.NODE_DESCRIPTOR_RESPONSE, buffer)).toStrictEqual([
Zdo.Status.SUCCESS,
{
nwkAddress: NODE_ID1,
logicalType: 0b010,
fragmentationSupported: undefined,
apsFlags: 0,
frequencyBand: 0b00100,
capabilities: {
alternatePANCoordinator: 0,
deviceType: 1,
powerSource: 1,
rxOnWhenIdle: 1,
reserved1: 0,
reserved2: 0,
securityCapability: 0,
allocateAddress: 0,
},
manufacturerCode: Zcl.ManufacturerCode.BARACODA_SA,
maxBufSize: 0x7c,
maxIncTxSize: 0x7eff,
serverMask: SERVER_MASK_R22,
maxOutTxSize: 0x3cff,
deprecated1: 0,
tlvs: [],
} as NodeDescriptorResponse,
]);
const tlv: FragmentationParametersGlobalTLV = {
nwkAddress: NODE_ID1,
fragmentationOptions: 1,
maxIncomingTransferUnit: 65352,
};
const buffer2 = Buffer.from([
1,
Zdo.Status.SUCCESS,
...NODE_ID1_BYTES,
0b00100010,
0b00100000,
0b00001110,
...uint16To8Array(Zcl.ManufacturerCode.BEIJING_RUYING_TECH_LIMITED),
0x3a,
...uint16To8Array(0x7cff),
...uint16To8Array(SERVER_MASK_R23_BYTE),
...uint16To8Array(0x11ff),
0,
Zdo.GlobalTLV.FRAGMENTATION_PARAMETERS,
5 - 1,
...uint16To8Array(tlv.nwkAddress),
tlv.fragmentationOptions!,
...uint16To8Array(tlv.maxIncomingTransferUnit!),
]);
expect(Zdo.Buffalo.readResponse(true, Zdo.ClusterId.NODE_DESCRIPTOR_RESPONSE, buffer2)).toStrictEqual([
Zdo.Status.SUCCESS,
{
nwkAddress: NODE_ID1,
logicalType: 0b010,
fragmentationSupported: true,
apsFlags: 0,
frequencyBand: 0b00100,
capabilities: {
alternatePANCoordinator: 0,
deviceType: 1,
powerSource: 1,
rxOnWhenIdle: 1,
reserved1: 0,
reserved2: 0,
securityCapability: 0,
allocateAddress: 0,
},
manufacturerCode: Zcl.ManufacturerCode.BEIJING_RUYING_TECH_LIMITED,
maxBufSize: 0x3a,
maxIncTxSize: 0x7cff,
serverMask: SERVER_MASK_R23,
maxOutTxSize: 0x11ff,
deprecated1: 0,
tlvs: [{tagId: Zdo