Skip to content

Commit ec701ac

Browse files
committed
Feature Map Attribute conformance and enablement as per the Device type features per endpoint
- Creating the feature table and populating it with data from the xml - Creating the data_type_feature table which references to data type cluster and feature ids. Populating it the same way as the data type attributes and data type commands. For now only adding those features which are of mandatory conformance for now. - Adding selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId to extract the features per endpoint type and cluster for setting the featureMap attribute appropriately. - Using the above function from query-config#insertOrUpdateAttributeState to update the featureMap attribute accordingly when its value is 0. - Start reporting featureMap attribute value warnings for each endpoint based on device type conformance - Adding unit tests for the device type cluster compliance for feature map attribute - JIRA: ZAPP-1346
1 parent 203522d commit ec701ac

14 files changed

+549
-20
lines changed

src-electron/db/db-mapping.js

+18
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,24 @@ exports.map = {
522522
deviceVersion: x.DEVICE_VERSION,
523523
}
524524
},
525+
endpointTypeDeviceExtended: (x) => {
526+
if (x == null) return undefined
527+
return {
528+
id: x.ENDPOINT_TYPE_DEVICE_ID,
529+
deviceTypeRef: x.DEVICE_TYPE_REF,
530+
endpointTypeRef: x.ENDPOINT_TYPE_REF,
531+
endpointTypeId: x.ENDPOINT_TYPE_REF,
532+
deviceTypeOrder: x.DEVICE_TYPE_ORDER,
533+
deviceIdentifier: x.DEVICE_IDENTIFIER,
534+
deviceId: x.DEVICE_IDENTIFIER,
535+
deviceVersion: x.DEVICE_VERSION,
536+
featureId: x.FEATURE_ID,
537+
featureCode: x.FEATURE_CODE,
538+
featureName: x.FEATURE_NAME,
539+
featureBit: x.FEATURE_BIT,
540+
clusterId: x.CLUSTER_REF,
541+
}
542+
},
525543
endpointTypeCluster: (x) => {
526544
if (x == null) return undefined
527545
return {

src-electron/db/query-config.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,28 @@ async function insertOrUpdateAttributeState(
213213
attributeId,
214214
clusterRef
215215
)
216+
// Looking for the feature map attribute in matter and setting it as per
217+
// the device types if default value is 0
218+
if (
219+
staticAttribute.code == 0xfffc &&
220+
staticAttribute.name == 'FeatureMap' &&
221+
staticAttribute.defaultValue == 0
222+
) {
223+
let featureMapDefaultValue = staticAttribute.defaultValue
224+
let mandatoryFeaturesOnEndpointTypeAndCluster =
225+
await queryDeviceType.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId(
226+
db,
227+
endpointTypeId,
228+
clusterRef
229+
)
230+
let featureMapBitsToBeEnabled =
231+
mandatoryFeaturesOnEndpointTypeAndCluster.map((f) => f.featureBit)
232+
featureMapBitsToBeEnabled.forEach(
233+
(featureBit) =>
234+
(featureMapDefaultValue = featureMapDefaultValue | (1 << featureBit))
235+
)
236+
staticAttribute.defaultValue = featureMapDefaultValue
237+
}
216238
let forcedExternal = await queryUpgrade.getForcedExternalStorage(db)
217239
staticAttribute.storagePolicy =
218240
await queryUpgrade.computeStoragePolicyNewConfig(
@@ -314,7 +336,7 @@ async function updateEndpointTypeAttribute(db, id, keyValuePairs) {
314336
})
315337
args.push(id)
316338

317-
let query = `UPDATE ENDPOINT_TYPE_ATTRIBUTE SET
339+
let query = `UPDATE ENDPOINT_TYPE_ATTRIBUTE SET
318340
${columns}
319341
WHERE ENDPOINT_TYPE_ATTRIBUTE_ID = ?`
320342
return dbApi.dbUpdate(db, query, args)
@@ -508,7 +530,7 @@ INTO ENDPOINT_TYPE_EVENT (
508530
db,
509531
`
510532
UPDATE ENDPOINT_TYPE_EVENT
511-
SET INCLUDED = ?
533+
SET INCLUDED = ?
512534
WHERE ENDPOINT_TYPE_REF = ?
513535
AND ENDPOINT_TYPE_CLUSTER_REF = ?
514536
AND EVENT_REF = ? `,
@@ -1372,17 +1394,17 @@ async function selectEndpointTypeAttributeId(
13721394
let rows = await dbApi.dbAll(
13731395
db,
13741396
`
1375-
SELECT
1397+
SELECT
13761398
ENDPOINT_TYPE_ATTRIBUTE_ID
1377-
FROM
1399+
FROM
13781400
ENDPOINT_TYPE_ATTRIBUTE AS ETA
13791401
INNER JOIN
13801402
ATTRIBUTE AS A
13811403
ON
1382-
ETA.ATTRIBUTE_REF = A.ATTRIBUTE_ID
1404+
ETA.ATTRIBUTE_REF = A.ATTRIBUTE_ID
13831405
INNER JOIN
13841406
CLUSTER AS C
1385-
ON
1407+
ON
13861408
C.CLUSTER_ID = A.CLUSTER_REF
13871409
WHERE
13881410
ETA.ENDPOINT_TYPE_REF = ?

src-electron/db/query-device-type.js

+90-1
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,36 @@ WHERE
312312
)
313313
}
314314

315+
/**
316+
* After loading up device type feature table with the names,
317+
* this method links the refererence to actual feature reference.
318+
*
319+
* @param {*} db
320+
* @returns promise of completion
321+
*/
322+
async function updateFeatureReferencesForDeviceTypeReferences(db, packageId) {
323+
return dbApi.dbUpdate(
324+
db,
325+
`
326+
UPDATE
327+
DEVICE_TYPE_FEATURE
328+
SET
329+
FEATURE_REF =
330+
( SELECT
331+
FEATURE.FEATURE_ID
332+
FROM
333+
FEATURE
334+
WHERE
335+
upper(FEATURE.CODE) = upper(DEVICE_TYPE_FEATURE.FEATURE_CODE)
336+
AND
337+
FEATURE.PACKAGE_REF = ?
338+
)
339+
WHERE
340+
DEVICE_TYPE_FEATURE.FEATURE_REF IS NULL`,
341+
[packageId]
342+
)
343+
}
344+
315345
/**
316346
* This method returns the promise of linking the device type clusters
317347
* commands and attributes to the correct IDs in the cluster, attribute
@@ -326,7 +356,8 @@ WHERE
326356
async function updateDeviceTypeEntityReferences(db, packageId) {
327357
await updateClusterReferencesForDeviceTypeClusters(db, packageId)
328358
await updateAttributeReferencesForDeviceTypeReferences(db, packageId)
329-
return updateCommandReferencesForDeviceTypeReferences(db, packageId)
359+
await updateCommandReferencesForDeviceTypeReferences(db, packageId)
360+
return updateFeatureReferencesForDeviceTypeReferences(db, packageId)
330361
}
331362

332363
/**
@@ -355,6 +386,62 @@ async function selectDeviceTypesByEndpointTypeId(db, endpointTypeId) {
355386
return rows.map(dbMapping.map.endpointTypeDevice)
356387
}
357388

389+
/**
390+
* Retrieves the device type features associated to an endpoint type id and cluster id
391+
* Note: Use clusterId as 'all' to get all features for an endpoint type id.
392+
* @param {*} db
393+
* @param {*} endpointTypeId
394+
* @param {*} clusterId
395+
* @returns promise with zcl device type feature information based on endpoint type id and cluster id
396+
*/
397+
async function selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId(
398+
db,
399+
endpointTypeId,
400+
clusterId
401+
) {
402+
let rows = await dbApi.dbAll(
403+
db,
404+
`
405+
SELECT
406+
ETD.ENDPOINT_TYPE_DEVICE_ID,
407+
ETD.DEVICE_TYPE_REF,
408+
ETD.ENDPOINT_TYPE_REF,
409+
ETD.DEVICE_TYPE_ORDER,
410+
ETD.DEVICE_IDENTIFIER,
411+
ETD.DEVICE_VERSION,
412+
FEATURE.FEATURE_ID,
413+
FEATURE.NAME AS FEATURE_NAME,
414+
FEATURE.CODE AS FEATURE_CODE,
415+
FEATURE.BIT AS FEATURE_BIT,
416+
DEVICE_TYPE_CLUSTER.CLUSTER_REF
417+
FROM
418+
ENDPOINT_TYPE_DEVICE AS ETD
419+
INNER JOIN
420+
DEVICE_TYPE
421+
ON
422+
ETD.DEVICE_TYPE_REF = DEVICE_TYPE.DEVICE_TYPE_ID
423+
INNER JOIN
424+
DEVICE_TYPE_CLUSTER
425+
ON
426+
DEVICE_TYPE_CLUSTER.DEVICE_TYPE_REF = DEVICE_TYPE.DEVICE_TYPE_ID
427+
INNER JOIN
428+
DEVICE_TYPE_FEATURE
429+
ON
430+
DEVICE_TYPE_FEATURE.DEVICE_TYPE_CLUSTER_REF = DEVICE_TYPE_CLUSTER.DEVICE_TYPE_CLUSTER_ID
431+
INNER JOIN
432+
FEATURE
433+
ON
434+
FEATURE.FEATURE_ID = DEVICE_TYPE_FEATURE.FEATURE_REF
435+
WHERE
436+
ETD.ENDPOINT_TYPE_REF = ${endpointTypeId}` +
437+
(clusterId != 'all'
438+
? ` AND
439+
DEVICE_TYPE_CLUSTER.CLUSTER_REF = ${clusterId}`
440+
: ``)
441+
)
442+
return rows.map(dbMapping.map.endpointTypeDeviceExtended)
443+
}
444+
358445
exports.selectAllDeviceTypes = selectAllDeviceTypes
359446
exports.selectDeviceTypeById = selectDeviceTypeById
360447
exports.selectDeviceTypeByCodeAndName = selectDeviceTypeByCodeAndName
@@ -369,3 +456,5 @@ exports.selectDeviceTypeCommandsByDeviceTypeRef =
369456
selectDeviceTypeCommandsByDeviceTypeRef
370457
exports.updateDeviceTypeEntityReferences = updateDeviceTypeEntityReferences
371458
exports.selectDeviceTypesByEndpointTypeId = selectDeviceTypesByEndpointTypeId
459+
exports.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId =
460+
selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId

src-electron/db/query-endpoint-type.js

+49-2
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,53 @@ WHERE
643643
return rows.map(mapFunction)
644644
}
645645

646+
/**
647+
* Given the endpoint type cluster id and attribute code, extract the value
648+
* endpoint type attribute information for that attribute within the endpoint
649+
* type cluster.
650+
* @param {*} db
651+
* @param {*} endpointTypeClusterId
652+
* @param {*} attributeCode
653+
* @param {*} attributeMfgCode
654+
*/
655+
async function selectEndpointTypeAttributeFromEndpointTypeClusterId(
656+
db,
657+
endpointTypeClusterId,
658+
attributeCode,
659+
attributeMfgCode
660+
) {
661+
let eta = await dbApi.dbGet(
662+
db,
663+
`
664+
SELECT
665+
ETC.ENDPOINT_TYPE_REF,
666+
ETC.CLUSTER_REF,
667+
ETA.INCLUDED,
668+
ETA.STORAGE_OPTION,
669+
ETA.SINGLETON,
670+
ETA.DEFAULT_VALUE
671+
FROM
672+
ATTRIBUTE AS A
673+
INNER JOIN
674+
ENDPOINT_TYPE_ATTRIBUTE AS ETA
675+
ON
676+
ETA.ATTRIBUTE_REF = A.ATTRIBUTE_ID
677+
INNER JOIN
678+
ENDPOINT_TYPE_CLUSTER AS ETC
679+
ON
680+
ETA.ENDPOINT_TYPE_CLUSTER_REF = ETC.ENDPOINT_TYPE_CLUSTER_ID
681+
WHERE
682+
ETC.ENDPOINT_TYPE_CLUSTER_ID = ${endpointTypeClusterId}
683+
AND
684+
A.CODE = ${attributeCode}
685+
` +
686+
(attributeMfgCode
687+
? ` AND A.MANUFACTURER_CODE = ${attributeMfgCode}`
688+
: ` AND A.MANUFACTURER_CODE IS NULL`)
689+
)
690+
return dbMapping.map.endpointTypeAttribute(eta)
691+
}
692+
646693
exports.deleteEndpointType = deleteEndpointType
647694
exports.selectAllEndpointTypes = selectAllEndpointTypes
648695
exports.selectEndpointTypeIds = selectEndpointTypeIds
@@ -652,13 +699,13 @@ exports.selectAllClustersDetailsFromEndpointTypes =
652699
selectAllClustersDetailsFromEndpointTypes
653700
exports.selectEndpointDetailsFromAddedEndpoints =
654701
selectEndpointDetailsFromAddedEndpoints
655-
656702
exports.selectAllClustersNamesFromEndpointTypes =
657703
selectAllClustersNamesFromEndpointTypes
658704
exports.selectAllClustersDetailsIrrespectiveOfSideFromEndpointTypes =
659705
selectAllClustersDetailsIrrespectiveOfSideFromEndpointTypes
660706
exports.selectCommandDetailsFromAllEndpointTypeCluster =
661707
selectCommandDetailsFromAllEndpointTypeCluster
662-
663708
exports.selectClustersAndEndpointDetailsFromEndpointTypes =
664709
selectClustersAndEndpointDetailsFromEndpointTypes
710+
exports.selectEndpointTypeAttributeFromEndpointTypeClusterId =
711+
selectEndpointTypeAttributeFromEndpointTypeClusterId

src-electron/db/query-loader.js

+55
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ async function insertClusters(db, packageId, data) {
618618
access: [],
619619
}
620620
let pTags = null
621+
let pFeatures = null
621622

622623
let i
623624
for (i = 0; i < lastIdsArray.length; i++) {
@@ -642,16 +643,45 @@ async function insertClusters(db, packageId, data) {
642643
if ('tags' in data[i]) {
643644
pTags = insertTags(db, packageId, data[i].tags, lastId)
644645
}
646+
647+
if ('features' in data[i]) {
648+
pFeatures = insertFeatures(db, packageId, data[i].features, lastId)
649+
}
645650
}
646651
let pCommand = insertCommands(db, packageId, commands)
647652
let pAttribute = insertAttributes(db, packageId, attributes)
648653
let pEvent = insertEvents(db, packageId, events)
649654
let pArray = [pCommand, pAttribute, pEvent]
650655
if (pTags != null) pArray.push(pTags)
656+
if (pFeatures != null) pArray.push(pFeatures)
651657
return Promise.all(pArray)
652658
})
653659
}
654660

661+
/**
662+
* Inserts features into the database.
663+
* @param {*} db
664+
* @param {*} packageId
665+
* @param {*} data
666+
* @returns A promise that resolves with array of rowids.
667+
*/
668+
async function insertFeatures(db, packageId, data, clusterId) {
669+
return dbApi.dbMultiInsert(
670+
db,
671+
'INSERT INTO FEATURE (PACKAGE_REF, NAME, CODE, BIT, DEFAULT_VALUE, DESCRIPTION, CONFORMANCE, CLUSTER_REF) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
672+
data.map((feature) => [
673+
packageId,
674+
feature.name,
675+
feature.code,
676+
feature.bit,
677+
feature.defaultValue,
678+
feature.description,
679+
feature.conformance,
680+
clusterId,
681+
])
682+
)
683+
}
684+
655685
/**
656686
* Inserts tags into the database.
657687
* data is an array of objects, containing 'name' and 'description'
@@ -895,6 +925,7 @@ async function insertDeviceTypes(db, packageId, data) {
895925
let promises = []
896926
promises.push(insertDeviceTypeAttributes(db, dtClusterRefDataPairs))
897927
promises.push(insertDeviceTypeCommands(db, dtClusterRefDataPairs))
928+
promises.push(insertDeviceTypeFeatures(db, dtClusterRefDataPairs))
898929
return Promise.all(promises)
899930
})
900931
}
@@ -903,6 +934,30 @@ async function insertDeviceTypes(db, packageId, data) {
903934
})
904935
}
905936

937+
/**
938+
* This handles the loading of device type feature requirements into the database.
939+
* There is a need to post-process to attach the actual feature ref after the fact
940+
* @param {*} db
941+
* @param {*} dtClusterRefDataPairs
942+
*/
943+
async function insertDeviceTypeFeatures(db, dtClusterRefDataPairs) {
944+
let features = []
945+
dtClusterRefDataPairs.map((dtClusterRefDataPair) => {
946+
let dtClusterRef = dtClusterRefDataPair.dtClusterRef
947+
let clusterData = dtClusterRefDataPair.clusterData
948+
if ('features' in clusterData) {
949+
clusterData.features.forEach((featureCode) => {
950+
features.push([dtClusterRef, featureCode])
951+
})
952+
}
953+
})
954+
return dbApi.dbMultiInsert(
955+
db,
956+
'INSERT INTO DEVICE_TYPE_FEATURE (DEVICE_TYPE_CLUSTER_REF, FEATURE_CODE) VALUES (?, ?)',
957+
features
958+
)
959+
}
960+
906961
/**
907962
* This handles the loading of device type attribute requirements into the database.
908963
* There is a need to post-process to attach the actual attribute ref after the fact

src-electron/db/query-zcl.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,8 @@ SELECT
10061006
ENDPOINT_TYPE_REF,
10071007
CLUSTER_REF,
10081008
SIDE,
1009-
ENABLED
1009+
ENABLED,
1010+
ENDPOINT_TYPE_CLUSTER_ID
10101011
FROM
10111012
ENDPOINT_TYPE_CLUSTER
10121013
WHERE

0 commit comments

Comments
 (0)