From 5f48bd5ce45b8cd91669a630ecab2b1294de8a41 Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:30:46 +0100 Subject: [PATCH] fix: Fill out missing Touchlink cluster info (#1300) --- src/zspec/zcl/definition/cluster.ts | 236 +++++++++++++- src/zspec/zcl/definition/enums.ts | 1 + src/zspec/zcl/definition/tstype.ts | 1 + src/zspec/zcl/zclFrame.ts | 4 + test/controller.test.ts | 486 ++-------------------------- test/zspec/zcl/frame.test.ts | 60 ++++ 6 files changed, 311 insertions(+), 477 deletions(-) diff --git a/src/zspec/zcl/definition/cluster.ts b/src/zspec/zcl/definition/cluster.ts index f7399ece91..3877215b01 100644 --- a/src/zspec/zcl/definition/cluster.ts +++ b/src/zspec/zcl/definition/cluster.ts @@ -3837,29 +3837,119 @@ export const Clusters: Readonly> attributes: {}, commands: { scanRequest: { - ID: 0, - response: 1, + ID: 0x00, + response: 0x01, parameters: [ {name: 'transactionID', type: DataType.UINT32}, {name: 'zigbeeInformation', type: DataType.BITMAP8}, {name: 'touchlinkInformation', type: DataType.BITMAP8}, ], }, + deviceInformation: { + ID: 0x02, + response: 0x03, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'startIndex', type: DataType.UINT8}, + ], + }, identifyRequest: { - ID: 6, + ID: 0x06, parameters: [ {name: 'transactionID', type: DataType.UINT32}, {name: 'duration', type: DataType.UINT16}, ], }, resetToFactoryNew: { - ID: 7, + ID: 0x07, parameters: [{name: 'transactionID', type: DataType.UINT32}], }, + networkStart: { + ID: 0x10, + response: 0x11, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'extendedPANID', type: DataType.IEEE_ADDR}, + {name: 'keyIndex', type: DataType.UINT8}, + {name: 'encryptedNetworkKey', type: DataType.SEC_KEY}, + {name: 'logicalChannel', type: DataType.UINT8}, + {name: 'panID', type: DataType.UINT16}, + {name: 'nwkAddr', type: DataType.UINT16}, + {name: 'groupIDsBegin', type: DataType.UINT16}, + {name: 'groupIDsEnd', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeBegin', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeEnd', type: DataType.UINT16}, + {name: 'freeGroupIDRangeBegin', type: DataType.UINT16}, + {name: 'freeGroupIDRangeEnd', type: DataType.UINT16}, + {name: 'initiatorIEEE', type: DataType.IEEE_ADDR}, + {name: 'initiatorNwkAddr', type: DataType.UINT16}, + ], + }, + networkJoinRouter: { + ID: 0x12, + response: 0x13, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'extendedPANID', type: DataType.IEEE_ADDR}, + {name: 'keyIndex', type: DataType.UINT8}, + {name: 'encryptedNetworkKey', type: DataType.SEC_KEY}, + {name: 'networkUpdateID', type: DataType.UINT8}, + {name: 'logicalChannel', type: DataType.UINT8}, + {name: 'panID', type: DataType.UINT16}, + {name: 'nwkAddr', type: DataType.UINT16}, + {name: 'groupIDsBegin', type: DataType.UINT16}, + {name: 'groupIDsEnd', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeBegin', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeEnd', type: DataType.UINT16}, + {name: 'freeGroupIDRangeBegin', type: DataType.UINT16}, + {name: 'freeGroupIDRangeEnd', type: DataType.UINT16}, + ], + }, + networkJoinEndDevice: { + ID: 0x14, + response: 0x15, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'extendedPANID', type: DataType.IEEE_ADDR}, + {name: 'keyIndex', type: DataType.UINT8}, + {name: 'encryptedNetworkKey', type: DataType.SEC_KEY}, + {name: 'networkUpdateID', type: DataType.UINT8}, + {name: 'logicalChannel', type: DataType.UINT8}, + {name: 'panID', type: DataType.UINT16}, + {name: 'nwkAddr', type: DataType.UINT16}, + {name: 'groupIDsBegin', type: DataType.UINT16}, + {name: 'groupIDsEnd', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeBegin', type: DataType.UINT16}, + {name: 'freeNwkAddrRangeEnd', type: DataType.UINT16}, + {name: 'freeGroupIDRangeBegin', type: DataType.UINT16}, + {name: 'freeGroupIDRangeEnd', type: DataType.UINT16}, + ], + }, + networkUpdate: { + ID: 0x16, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'extendedPANID', type: DataType.IEEE_ADDR}, + {name: 'networkUpdateID', type: DataType.UINT8}, + {name: 'logicalChannel', type: DataType.UINT8}, + {name: 'panID', type: DataType.UINT16}, + {name: 'nwkAddr', type: DataType.UINT16}, + ], + }, + getGroupIdentifiers: { + ID: 0x41, + response: 0x41, + parameters: [{name: 'startIndex', type: DataType.UINT8}], + }, + getEndpointList: { + ID: 0x42, + response: 0x42, + parameters: [{name: 'startIndex', type: DataType.UINT8}], + }, }, commandsResponse: { scanResponse: { - ID: 1, + ID: 0x01, parameters: [ {name: 'transactionID', type: DataType.UINT32}, {name: 'rssiCorrection', type: DataType.UINT8}, @@ -3874,15 +3964,133 @@ export const Clusters: Readonly> {name: 'networkAddress', type: DataType.UINT16}, {name: 'numberOfSubDevices', type: DataType.UINT8}, {name: 'totalGroupIdentifiers', type: DataType.UINT8}, - // Below are optional according to the spec, not all devices send these. - // e.g. https://github.com/Koenkk/zigbee2mqtt/issues/8535#issuecomment-909199162 - // Since we don't have a way to deal with optional attributes yet and since we don't - // use these attributes anyway, disable them for now. - // {name: 'endpointID', type: DataType.UINT8}, - // {name: 'profileID', type: DataType.UINT16}, - // {name: 'deviceID', type: DataType.UINT16}, - // {name: 'version', type: DataType.UINT8}, - // {name: 'groupIdentifierCount', type: DataType.UINT8}, + { + name: 'endpointID', + type: DataType.UINT8, + conditions: [{type: ParameterCondition.FIELD_EQUAL, field: 'numberOfSubDevices', value: 1}], + }, + { + name: 'profileID', + type: DataType.UINT16, + conditions: [{type: ParameterCondition.FIELD_EQUAL, field: 'numberOfSubDevices', value: 1}], + }, + { + name: 'deviceID', + type: DataType.UINT16, + conditions: [{type: ParameterCondition.FIELD_EQUAL, field: 'numberOfSubDevices', value: 1}], + }, + { + name: 'version', + type: DataType.UINT8, + conditions: [{type: ParameterCondition.FIELD_EQUAL, field: 'numberOfSubDevices', value: 1}], + }, + { + name: 'groupIDCount', + type: DataType.UINT8, + conditions: [{type: ParameterCondition.FIELD_EQUAL, field: 'numberOfSubDevices', value: 1}], + }, + ], + }, + deviceInformation: { + ID: 0x03, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + {name: 'numberOfSubDevices', type: DataType.UINT8}, + {name: 'startIndex', type: DataType.UINT8}, + {name: 'deviceInfoCount', type: DataType.UINT8}, + /** + * TODO: (this * deviceInfoCount) + * {name: 'ieeeAddress', type: DataType.IEEE_ADDR}, + * {name: 'endpointID', type: DataType.UINT8}, + * {name: 'profileID', type: DataType.UINT16}, + * {name: 'deviceID', type: DataType.UINT16}, + * {name: 'version', type: DataType.UINT8}, + * {name: 'groupIdCount', type: DataType.UINT8}, + * {name: 'sort', type: DataType.UINT8}, + */ + // {name: 'deviceInfoRecord', type: TODO}, + ], + }, + networkStart: { + ID: 0x11, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + /** + * - 0x00 Success + * - 0x01 Failure + * - 0x02 – 0xff Reserved + */ + {name: 'status', type: DataType.ENUM8}, + {name: 'extendedPANID', type: DataType.IEEE_ADDR}, + {name: 'networkUpdateID', type: DataType.UINT8}, + {name: 'logicalChannel', type: DataType.UINT8}, + {name: 'panID', type: DataType.UINT16}, + ], + }, + networkJoinRouter: { + ID: 0x13, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + /** + * - 0x00 Success + * - 0x01 Failure + * - 0x02 – 0xff Reserved + */ + {name: 'status', type: DataType.ENUM8}, + ], + }, + networkJoinEndDevice: { + ID: 0x15, + parameters: [ + {name: 'transactionID', type: DataType.UINT32}, + /** + * - 0x00 Success + * - 0x01 Failure + * - 0x02 – 0xff Reserved + */ + {name: 'status', type: DataType.ENUM8}, + ], + }, + endpointInformation: { + ID: 0x40, + parameters: [ + {name: 'ieeeAddress', type: DataType.IEEE_ADDR}, + {name: 'networkAddress', type: DataType.UINT16}, + {name: 'endpointID', type: DataType.UINT8}, + {name: 'profileID', type: DataType.UINT16}, + {name: 'deviceID', type: DataType.UINT16}, + {name: 'version', type: DataType.UINT8}, + ], + }, + getGroupIdentifiers: { + ID: 0x41, + parameters: [ + {name: 'total', type: DataType.UINT8}, + {name: 'startIndex', type: DataType.UINT8}, + {name: 'count', type: DataType.UINT8}, + /** + * TODO: (this * count) + * {name: 'groupID', type: DataType.UINT16}, + * {name: 'groupType', type: DataType.UINT8}, + */ + // {name: 'groupInfoList', type: TODO}, + ], + }, + getEndpointList: { + ID: 0x42, + parameters: [ + {name: 'total', type: DataType.UINT8}, + {name: 'startIndex', type: DataType.UINT8}, + {name: 'count', type: DataType.UINT8}, + /** + * TODO: (this * count) + * {name: 'networkAddress', type: DataType.UINT16}, + * {name: 'endpointID', type: DataType.UINT8}, + * {name: 'profileID', type: DataType.UINT16}, + * {name: 'deviceID', type: DataType.UINT16}, + * {name: 'version', type: DataType.UINT8}, + */ + // {name: 'endpointInfoList', type: TODO}, ], }, }, diff --git a/src/zspec/zcl/definition/enums.ts b/src/zspec/zcl/definition/enums.ts index ec0507679a..0b19b79cdf 100644 --- a/src/zspec/zcl/definition/enums.ts +++ b/src/zspec/zcl/definition/enums.ts @@ -164,6 +164,7 @@ export enum ParameterCondition { BITMASK_SET = 'bitMaskSet', BITFIELD_ENUM = 'bitFieldEnum', DATA_TYPE_CLASS_EQUAL = 'dataTypeValueTypeEquals', + FIELD_EQUAL = 'fieldEquals', } export enum FrameType { diff --git a/src/zspec/zcl/definition/tstype.ts b/src/zspec/zcl/definition/tstype.ts index bfc44e6565..bd7680d128 100644 --- a/src/zspec/zcl/definition/tstype.ts +++ b/src/zspec/zcl/definition/tstype.ts @@ -65,6 +65,7 @@ export interface ParameterDefinition extends Parameter { | {type: ParameterCondition.BITMASK_SET; param: string; mask: number} | {type: ParameterCondition.BITFIELD_ENUM; param: string; offset: number; size: number; value: number} | {type: ParameterCondition.DATA_TYPE_CLASS_EQUAL; value: DataTypeClass} + | {type: ParameterCondition.FIELD_EQUAL; field: string; value: unknown} )[]; } diff --git a/src/zspec/zcl/zclFrame.ts b/src/zspec/zcl/zclFrame.ts index 58a0ca2359..24b4a24710 100644 --- a/src/zspec/zcl/zclFrame.ts +++ b/src/zspec/zcl/zclFrame.ts @@ -307,6 +307,10 @@ export class ZclFrame { if (Utils.getDataTypeClass(entry.dataType) !== condition.value) return false; break; } + case ParameterCondition.FIELD_EQUAL: { + if (entry[condition.field] !== condition.value) return false; + break; + } } } } diff --git a/test/controller.test.ts b/test/controller.test.ts index 26f1b15bbd..e311b00b27 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -738,48 +738,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -803,48 +763,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -870,48 +790,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 6, @@ -933,48 +813,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, }); @@ -1009,48 +849,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -1074,48 +874,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -1174,48 +934,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -1241,48 +961,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 6, @@ -1304,48 +984,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, }); @@ -1372,48 +1012,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 0, @@ -1439,48 +1039,8 @@ describe('Controller', () => { ID: 4096, attributes: {}, name: 'touchlink', - commands: { - scanRequest: { - ID: 0, - response: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'zigbeeInformation', type: 24}, - {name: 'touchlinkInformation', type: 24}, - ], - name: 'scanRequest', - }, - identifyRequest: { - ID: 6, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'duration', type: 33}, - ], - name: 'identifyRequest', - }, - resetToFactoryNew: {ID: 7, parameters: [{name: 'transactionID', type: 35}], name: 'resetToFactoryNew'}, - }, - commandsResponse: { - scanResponse: { - ID: 1, - parameters: [ - {name: 'transactionID', type: 35}, - {name: 'rssiCorrection', type: 32}, - {name: 'zigbeeInformation', type: 32}, - {name: 'touchlinkInformation', type: 32}, - {name: 'keyBitmask', type: 33}, - {name: 'responseID', type: 35}, - {name: 'extendedPanID', type: 240}, - {name: 'networkUpdateID', type: 32}, - {name: 'logicalChannel', type: 32}, - {name: 'panID', type: 33}, - {name: 'networkAddress', type: 33}, - {name: 'numberOfSubDevices', type: 32}, - {name: 'totalGroupIdentifiers', type: 32}, - ], - name: 'scanResponse', - }, - }, + commands: expect.any(Object), + commandsResponse: expect.any(Object), }, command: { ID: 6, @@ -1514,7 +1074,7 @@ describe('Controller', () => { logicalChannel: 1, panID: 1, networkAddress: 1, - numberOfSubDevices: 1, + numberOfSubDevices: 0, totalGroupIdentifiers: 1, }, {}, diff --git a/test/zspec/zcl/frame.test.ts b/test/zspec/zcl/frame.test.ts index d7e5fe1f5e..8caa6343f7 100644 --- a/test/zspec/zcl/frame.test.ts +++ b/test/zspec/zcl/frame.test.ts @@ -356,6 +356,18 @@ describe('ZCL Frame', () => { ), ).toBeFalsy(); }); + + it('FIELD_EQUAL', () => { + expect( + Zcl.Frame.conditionsValid(Zcl.Clusters.touchlink.commandsResponse.scanResponse.parameters[13], {numberOfSubDevices: 1}, null), + ).toBeTruthy(); + expect( + Zcl.Frame.conditionsValid(Zcl.Clusters.touchlink.commandsResponse.scanResponse.parameters[13], {numberOfSubDevices: 0}, null), + ).toBeFalsy(); + expect( + Zcl.Frame.conditionsValid(Zcl.Clusters.touchlink.commandsResponse.scanResponse.parameters[13], {numberOfSubDevices: 3}, null), + ).toBeFalsy(); + }); }); describe('Header', () => { @@ -862,4 +874,52 @@ describe('ZCL Frame', () => { expect(frame.payload).toStrictEqual(expected); }); + + it('Reads/writes Touchlink Scan Response with different size payloads', () => { + const full = Buffer.from([ + 0x19, 0x0, 0x1, 0xe0, 0xde, 0x5e, 0x2f, 0xa, 0x5, 0x0, 0x12, 0x0, 0x3d, 0x30, 0x1d, 0x4a, 0x8f, 0xb7, 0xdc, 0x1c, 0x0, 0x4b, 0x12, 0x0, + 0x0, 0xf, 0x62, 0x1a, 0xb3, 0xaa, 0x1, 0x0, 0xb, 0x5e, 0xc0, 0x10, 0x2, 0x2, 0x0, + ]); + const short = Buffer.from([ + 0x19, 0x0, 0x1, 0xe0, 0xde, 0x5e, 0x2f, 0xa, 0x5, 0x0, 0x12, 0x0, 0x3d, 0x30, 0x1d, 0x4a, 0x8f, 0xb7, 0xdc, 0x1c, 0x0, 0x4b, 0x12, 0x0, + 0x0, 0xf, 0x62, 0x1a, 0xb3, 0xaa, 0x0, 0x0, + ]); + const short2 = Buffer.from([ + 0x19, 0x0, 0x1, 0xe0, 0xde, 0x5e, 0x2f, 0xa, 0x5, 0x0, 0x12, 0x0, 0x3d, 0x30, 0x1d, 0x4a, 0x8f, 0xb7, 0xdc, 0x1c, 0x0, 0x4b, 0x12, 0x0, + 0x0, 0xf, 0x62, 0x1a, 0xb3, 0xaa, 0x3, 0x0, + ]); + + const fullZcl = Zcl.Frame.fromBuffer(0x1000, Zcl.Header.fromBuffer(full), full, {}); + expect(fullZcl.payload.numberOfSubDevices).toStrictEqual(1); + expect(fullZcl.payload.endpointID).toStrictEqual(11); + expect(fullZcl.payload.profileID).toStrictEqual(0xc05e); + expect(fullZcl.payload.deviceID).toStrictEqual(0x0210); + expect(fullZcl.payload.version).toStrictEqual(0x02); + expect(fullZcl.payload.groupIDCount).toStrictEqual(0); + + const shortZcl = Zcl.Frame.fromBuffer(0x1000, Zcl.Header.fromBuffer(short), short, {}); + expect(shortZcl.payload.numberOfSubDevices).toStrictEqual(0); + expect(shortZcl.payload.endpointID).toStrictEqual(undefined); + expect(shortZcl.payload.profileID).toStrictEqual(undefined); + expect(shortZcl.payload.deviceID).toStrictEqual(undefined); + expect(shortZcl.payload.version).toStrictEqual(undefined); + expect(shortZcl.payload.groupIDCount).toStrictEqual(undefined); + + const short2Zcl = Zcl.Frame.fromBuffer(0x1000, Zcl.Header.fromBuffer(short2), short2, {}); + expect(short2Zcl.payload.numberOfSubDevices).toStrictEqual(3); + expect(short2Zcl.payload.endpointID).toStrictEqual(undefined); + expect(short2Zcl.payload.profileID).toStrictEqual(undefined); + expect(short2Zcl.payload.deviceID).toStrictEqual(undefined); + expect(short2Zcl.payload.version).toStrictEqual(undefined); + expect(short2Zcl.payload.groupIDCount).toStrictEqual(undefined); + + const fullNew = fullZcl.toBuffer(); + expect(fullNew).toStrictEqual(full); + + const shortNew = shortZcl.toBuffer(); + expect(shortNew).toStrictEqual(short); + + const short2New = short2Zcl.toBuffer(); + expect(short2New).toStrictEqual(short2); + }); });