Skip to content

Commit 2498871

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 - Adding features in cluster xml as a bitmap such that bitmap name='feature' can be removed from the xml in favor of features which holds the featuremap information as well as defines the feature bitmap such that there is no duplicate information between features and bitmap name=feature - Showing the correct UI based on category from json files when there is only one zcl and template package and user does not have any packages to select from. - Making sure the same named feature codes which exist on different bits of the cluster are handled correctly - Updating the zap schema as per the new changes - JIRA: ZAPP-1346
1 parent 267c13e commit 2498871

21 files changed

+5042
-4095
lines changed

apack.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Graphical configuration tool for application and libraries based on Zigbee Cluster Library.",
55
"path": [".", "node_modules/.bin/", "ZAP.app/Contents/MacOS"],
66
"requiredFeatureLevel": "apack.core:9",
7-
"featureLevel": 101,
7+
"featureLevel": 102,
88
"uc.triggerExtension": "zap",
99
"executable": {
1010
"zap:win32.x86_64": {

docs/zap-schema.svg

+2,107-2,109
Loading

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

+99-1
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,45 @@ 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.CLUSTER_REF = (
338+
SELECT
339+
DEVICE_TYPE_CLUSTER.CLUSTER_REF
340+
FROM
341+
DEVICE_TYPE_CLUSTER
342+
WHERE
343+
DEVICE_TYPE_CLUSTER_ID = DEVICE_TYPE_FEATURE.DEVICE_TYPE_CLUSTER_REF
344+
)
345+
AND
346+
FEATURE.PACKAGE_REF = ?
347+
)
348+
WHERE
349+
DEVICE_TYPE_FEATURE.FEATURE_REF IS NULL`,
350+
[packageId]
351+
)
352+
}
353+
315354
/**
316355
* This method returns the promise of linking the device type clusters
317356
* commands and attributes to the correct IDs in the cluster, attribute
@@ -326,7 +365,8 @@ WHERE
326365
async function updateDeviceTypeEntityReferences(db, packageId) {
327366
await updateClusterReferencesForDeviceTypeClusters(db, packageId)
328367
await updateAttributeReferencesForDeviceTypeReferences(db, packageId)
329-
return updateCommandReferencesForDeviceTypeReferences(db, packageId)
368+
await updateCommandReferencesForDeviceTypeReferences(db, packageId)
369+
return updateFeatureReferencesForDeviceTypeReferences(db, packageId)
330370
}
331371

332372
/**
@@ -355,6 +395,62 @@ async function selectDeviceTypesByEndpointTypeId(db, endpointTypeId) {
355395
return rows.map(dbMapping.map.endpointTypeDevice)
356396
}
357397

398+
/**
399+
* Retrieves the device type features associated to an endpoint type id and cluster id
400+
* Note: Use clusterId as 'all' to get all features for an endpoint type id.
401+
* @param {*} db
402+
* @param {*} endpointTypeId
403+
* @param {*} clusterId
404+
* @returns promise with zcl device type feature information based on endpoint type id and cluster id
405+
*/
406+
async function selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId(
407+
db,
408+
endpointTypeId,
409+
clusterId
410+
) {
411+
let rows = await dbApi.dbAll(
412+
db,
413+
`
414+
SELECT
415+
ETD.ENDPOINT_TYPE_DEVICE_ID,
416+
ETD.DEVICE_TYPE_REF,
417+
ETD.ENDPOINT_TYPE_REF,
418+
ETD.DEVICE_TYPE_ORDER,
419+
ETD.DEVICE_IDENTIFIER,
420+
ETD.DEVICE_VERSION,
421+
FEATURE.FEATURE_ID,
422+
FEATURE.NAME AS FEATURE_NAME,
423+
FEATURE.CODE AS FEATURE_CODE,
424+
FEATURE.BIT AS FEATURE_BIT,
425+
DEVICE_TYPE_CLUSTER.CLUSTER_REF
426+
FROM
427+
ENDPOINT_TYPE_DEVICE AS ETD
428+
INNER JOIN
429+
DEVICE_TYPE
430+
ON
431+
ETD.DEVICE_TYPE_REF = DEVICE_TYPE.DEVICE_TYPE_ID
432+
INNER JOIN
433+
DEVICE_TYPE_CLUSTER
434+
ON
435+
DEVICE_TYPE_CLUSTER.DEVICE_TYPE_REF = DEVICE_TYPE.DEVICE_TYPE_ID
436+
INNER JOIN
437+
DEVICE_TYPE_FEATURE
438+
ON
439+
DEVICE_TYPE_FEATURE.DEVICE_TYPE_CLUSTER_REF = DEVICE_TYPE_CLUSTER.DEVICE_TYPE_CLUSTER_ID
440+
INNER JOIN
441+
FEATURE
442+
ON
443+
FEATURE.FEATURE_ID = DEVICE_TYPE_FEATURE.FEATURE_REF
444+
WHERE
445+
ETD.ENDPOINT_TYPE_REF = ${endpointTypeId}` +
446+
(clusterId != 'all'
447+
? ` AND
448+
DEVICE_TYPE_CLUSTER.CLUSTER_REF = ${clusterId}`
449+
: ``)
450+
)
451+
return rows.map(dbMapping.map.endpointTypeDeviceExtended)
452+
}
453+
358454
exports.selectAllDeviceTypes = selectAllDeviceTypes
359455
exports.selectDeviceTypeById = selectDeviceTypeById
360456
exports.selectDeviceTypeByCodeAndName = selectDeviceTypeByCodeAndName
@@ -369,3 +465,5 @@ exports.selectDeviceTypeCommandsByDeviceTypeRef =
369465
selectDeviceTypeCommandsByDeviceTypeRef
370466
exports.updateDeviceTypeEntityReferences = updateDeviceTypeEntityReferences
371467
exports.selectDeviceTypesByEndpointTypeId = selectDeviceTypesByEndpointTypeId
468+
exports.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId =
469+
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 && clusterData.features.length > 0) {
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

0 commit comments

Comments
 (0)