Skip to content

Commit

Permalink
Feature Page Phase 2 (project-chip#1482)
Browse files Browse the repository at this point in the history
- Enable users to toggle device type features.
- When toggling a feature, automatically update the associated elements (attributes, commands, events) to correct conformance, and update the feature map attribute.
- Show warnings and disable feature toggle if relevant elements have conformance that is unknown or too complex to handle.
- Show detailed conformance warnings for elements if they're enabled state conflicts with conformance value.
  • Loading branch information
ethanzhouyc authored Nov 22, 2024
1 parent 3879080 commit c72231e
Show file tree
Hide file tree
Showing 34 changed files with 7,977 additions and 5,100 deletions.
570 changes: 487 additions & 83 deletions docs/api.md

Large diffs are not rendered by default.

4,817 changes: 2,396 additions & 2,421 deletions docs/zap-schema.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 47 additions & 5 deletions src-electron/db/db-mapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ exports.map = {
type: x.TYPE != 'array' ? x.TYPE : x.ARRAY_TYPE,
side: x.SIDE,
define: x.DEFINE,
conformance: x.CONFORMANCE,
min: x.MIN,
max: x.MAX,
minLength: x.MIN_LENGTH,
Expand Down Expand Up @@ -176,6 +177,7 @@ exports.map = {
name: x.NAME,
description: x.DESCRIPTION,
side: x.SIDE,
conformance: x.CONFORMANCE,
isOptional: dbApi.fromDbBool(x.IS_OPTIONAL),
isFabricSensitive: dbApi.fromDbBool(x.IS_FABRIC_SENSITIVE),
priority: x.PRIORITY
Expand All @@ -196,6 +198,7 @@ exports.map = {
description: x.DESCRIPTION,
source: x.SOURCE,
isOptional: dbApi.fromDbBool(x.IS_OPTIONAL),
conformance: x.CONFORMANCE,
mustUseTimedInvoke: dbApi.fromDbBool(x.MUST_USE_TIMED_INVOKE),
isFabricScoped: dbApi.fromDbBool(x.IS_FABRIC_SCOPED),
clusterCode: x.CLUSTER_CODE,
Expand Down Expand Up @@ -251,16 +254,20 @@ exports.map = {
if (x == null) return undefined
return {
deviceType: x.DEVICE_TYPE_NAME,
deviceTypeClusterId: x.DEVICE_TYPE_CLUSTER_ID,
clusterRef: x.CLUSTER_REF,
cluster: x.CLUSTER_NAME,
includeServer: x.INCLUDE_SERVER,
includeClient: x.INCLUDE_CLIENT,
conformance: x.DEVICE_TYPE_CLUSTER_CONFORMANCE,
id: x.FEATURE_ID,
featureId: x.FEATURE_ID,
name: x.FEATURE_NAME,
code: x.CODE,
bit: x.BIT,
default_value: x.DEFAULT_VALUE,
description: x.DESCRIPTION
description: x.DESCRIPTION,
endpointTypeClusterId: x.ENDPOINT_TYPE_CLUSTER_ID,
featureMapAttributeId: x.FEATURE_MAP_ATTRIBUTE_ID,
featureMapValue: x.FEATURE_MAP_VALUE
}
},

Expand Down Expand Up @@ -585,7 +592,8 @@ exports.map = {
featureName: x.FEATURE_NAME,
featureBit: x.FEATURE_BIT,
clusterId: x.CLUSTER_REF,
composition: x.TYPE
composition: x.TYPE,
conformance: x.DEVICE_TYPE_CLUSTER_CONFORMANCE
}
},
endpointTypeCluster: (x) => {
Expand Down Expand Up @@ -692,7 +700,10 @@ exports.map = {
type: x.TYPE != 'array' ? x.TYPE : x.ARRAY_TYPE, // Attribute type
apiMaturity: x.API_MATURITY,
isChangeOmitted: dbApi.fromDbBool(x.IS_CHANGE_OMITTED),
persistence: x.PERSISTENCE
persistence: x.PERSISTENCE,
reportMinInterval: x.REPORT_MIN_INTERVAL,
reportMaxInterval: x.REPORT_MAX_INTERVAL,
conformance: x.CONFORMANCE
}
},

Expand All @@ -709,6 +720,23 @@ exports.map = {
}
},

endpointTypeCommandExtended: (x) => {
if (x == null) return undefined
return {
id: x.COMMAND_ID,
name: x.NAME, // Command Name
clusterRef: x.CLUSTER_REF,
commandRef: x.COMMAND_REF,
incoming: dbApi.fromDbBool(x.INCOMING),
outgoing: dbApi.fromDbBool(x.OUTGOING),
isIncoming: dbApi.fromDbBool(x.IS_INCOMING),
source: x.SOURCE,
conformance: x.CONFORMANCE,
endpointTypeRef: x.ENDPOINT_TYPE_REF,
isEnabled: dbApi.fromDbBool(x.IS_ENABLED)
}
},

endpointTypeEvent: (x) => {
if (x == null) return undefined
return {
Expand All @@ -719,6 +747,20 @@ exports.map = {
}
},

endpointTypeEventExtended: (x) => {
if (x == null) return undefined
return {
id: x.EVENT_ID,
name: x.NAME, // Event Name
clusterRef: x.CLUSTER_REF,
eventRef: x.EVENT_REF,
side: x.SIDE,
conformance: x.CONFORMANCE,
endpointTypeRef: x.ENDPOINT_TYPE_REF,
included: dbApi.fromDbBool(x.INCLUDED)
}
},

packageExtension: (x) => {
if (x == null) return undefined
return {
Expand Down
39 changes: 39 additions & 0 deletions src-electron/db/query-attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,43 @@ async function selectAttributeMappingsByPackageIds(db, packageIds) {
return rows.map(dbMapping.map.attributeMapping)
}

/**
* Get all attributes in an endpoint type cluster
* @param {*} db
* @param {*} endpointTyeClusterId
* @returns all attributes in an endpoint type cluster
*/
async function selectAttributesByEndpointTypeClusterId(
db,
endpointTyeClusterId
) {
let rows = await dbApi.dbAll(
db,
`
SELECT
ATTRIBUTE.ATTRIBUTE_ID,
ATTRIBUTE.NAME,
ATTRIBUTE.CLUSTER_REF,
ATTRIBUTE.SIDE,
ATTRIBUTE.CONFORMANCE,
ATTRIBUTE.REPORT_MIN_INTERVAL,
ATTRIBUTE.REPORT_MAX_INTERVAL,
ATTRIBUTE.REPORTABLE_CHANGE,
ENDPOINT_TYPE_ATTRIBUTE.INCLUDED
FROM
ATTRIBUTE
JOIN
ENDPOINT_TYPE_ATTRIBUTE
ON
ATTRIBUTE.ATTRIBUTE_ID = ENDPOINT_TYPE_ATTRIBUTE.ATTRIBUTE_REF
WHERE
ENDPOINT_TYPE_ATTRIBUTE.ENDPOINT_TYPE_CLUSTER_REF = ?
`,
[endpointTyeClusterId]
)
return rows.map(dbMapping.map.endpointTypeAttributeExtended)
}

exports.selectAllAttributeDetailsFromEnabledClusters = dbCache.cacheQuery(
selectAllAttributeDetailsFromEnabledClusters
)
Expand All @@ -1274,3 +1311,5 @@ exports.selectTokenAttributesForEndpoint = selectTokenAttributesForEndpoint
exports.selectAllUserTokenAttributes = selectAllUserTokenAttributes
exports.selectAttributeMappingsByPackageIds =
selectAttributeMappingsByPackageIds
exports.selectAttributesByEndpointTypeClusterId =
selectAttributesByEndpointTypeClusterId
48 changes: 48 additions & 0 deletions src-electron/db/query-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ SELECT
COMMAND.DESCRIPTION,
COMMAND.SOURCE,
COMMAND.IS_OPTIONAL,
COMMAND.CONFORMANCE,
COMMAND.MUST_USE_TIMED_INVOKE,
COMMAND.IS_FABRIC_SCOPED,
COMMAND.RESPONSE_REF,
Expand Down Expand Up @@ -1326,6 +1327,7 @@ SELECT
COMMAND.DESCRIPTION,
COMMAND.SOURCE,
COMMAND.IS_OPTIONAL,
COMMAND.CONFORMANCE,
COMMAND.MUST_USE_TIMED_INVOKE,
COMMAND.IS_FABRIC_SCOPED,
COMMAND.RESPONSE_REF,
Expand Down Expand Up @@ -2046,6 +2048,50 @@ async function selectNonManufacturerSpecificCommandDetailsFromAllEndpointTypesAn
)
}

/**
* Get all commands in an endpoint type cluster
* Non-required commands are not loaded into ENDPOINT_TYPE_COMMAND table,
* so we need to load all commands by joining DEVICE_TYPE_COMMAND table
* @param {*} db
* @param {*} endpointTypeClusterId
* @param {*} deviceTypeClusterId
* @returns all commands in an endpoint type cluster
*/
async function selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId(
db,
endpointTypeClusterId,
deviceTypeClusterId
) {
let rows = await dbApi.dbAll(
db,
`
SELECT
COMMAND.COMMAND_ID,
COMMAND.NAME,
COMMAND.CLUSTER_REF,
COMMAND.SOURCE,
COMMAND.CONFORMANCE,
COALESCE(ENDPOINT_TYPE_COMMAND.IS_ENABLED, 0) AS IS_ENABLED
FROM
COMMAND
JOIN
DEVICE_TYPE_CLUSTER
ON
COMMAND.CLUSTER_REF = DEVICE_TYPE_CLUSTER.CLUSTER_REF
LEFT JOIN
ENDPOINT_TYPE_COMMAND
ON
COMMAND.COMMAND_ID = ENDPOINT_TYPE_COMMAND.COMMAND_REF
AND
ENDPOINT_TYPE_COMMAND.ENDPOINT_TYPE_CLUSTER_REF = ?
WHERE
DEVICE_TYPE_CLUSTER.DEVICE_TYPE_CLUSTER_ID = ?
`,
[endpointTypeClusterId, deviceTypeClusterId]
)
return rows.map(dbMapping.map.endpointTypeCommandExtended)
}

exports.selectCliCommandCountFromEndpointTypeCluster =
selectCliCommandCountFromEndpointTypeCluster
exports.selectCliCommandsFromCluster = selectCliCommandsFromCluster
Expand Down Expand Up @@ -2098,3 +2144,5 @@ exports.selectAllOutgoingCommandsForCluster =
exports.selectEndpointTypeCommandsByEndpointTypeRefAndClusterRef =
selectEndpointTypeCommandsByEndpointTypeRefAndClusterRef
exports.duplicateEndpointTypeCommand = duplicateEndpointTypeCommand
exports.selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId =
selectCommandsByEndpointTypeClusterIdAndDeviceTypeClusterId
8 changes: 5 additions & 3 deletions src-electron/db/query-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,16 @@ async function insertOrUpdateAttributeState(
staticAttribute.defaultValue == 0
) {
let featureMapDefaultValue = staticAttribute.defaultValue
let mandatoryFeaturesOnEndpointTypeAndCluster =
let featuresOnEndpointTypeAndCluster =
await queryDeviceType.selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId(
db,
endpointTypeId,
clusterRef
)
let featureMapBitsToBeEnabled =
mandatoryFeaturesOnEndpointTypeAndCluster.map((f) => f.featureBit)
// only set featureMap bit to 1 for mandatory features
let featureMapBitsToBeEnabled = featuresOnEndpointTypeAndCluster
.filter((f) => f.conformance == 'M')
.map((f) => f.featureBit)
featureMapBitsToBeEnabled.forEach(
(featureBit) =>
(featureMapDefaultValue = featureMapDefaultValue | (1 << featureBit))
Expand Down
3 changes: 2 additions & 1 deletion src-electron/db/query-device-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ async function selectDeviceTypeFeaturesByEndpointTypeIdAndClusterId(
FEATURE.NAME AS FEATURE_NAME,
FEATURE.CODE AS FEATURE_CODE,
FEATURE.BIT AS FEATURE_BIT,
DEVICE_TYPE_CLUSTER.CLUSTER_REF
DEVICE_TYPE_CLUSTER.CLUSTER_REF,
DEVICE_TYPE_FEATURE.DEVICE_TYPE_CLUSTER_CONFORMANCE
FROM
ENDPOINT_TYPE_DEVICE AS ETD
INNER JOIN
Expand Down
49 changes: 49 additions & 0 deletions src-electron/db/query-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ SELECT
NAME,
DESCRIPTION,
SIDE,
CONFORMANCE,
IS_OPTIONAL,
IS_FABRIC_SENSITIVE,
PRIORITY
Expand Down Expand Up @@ -148,6 +149,7 @@ SELECT
E.NAME,
E.DESCRIPTION,
E.SIDE,
E.CONFORMANCE,
E.IS_OPTIONAL,
E.IS_FABRIC_SENSITIVE,
E.PRIORITY
Expand Down Expand Up @@ -231,9 +233,56 @@ ORDER BY
.then((rows) => rows.map(dbMapping.map.eventField))
}

/**
* Get all events in an endpoint type cluster
* Disabled events are not loaded into ENDPOINT_TYPE_EVENT table,
* so we need to load all events by joining DEVICE_TYPE_EVENT table
*
* @param {*} db
* @param {*} endpointTypeClusterId
* @param {*} deviceTypeClusterId
* @returns all events in an endpoint type cluster
*/
async function selectEventsByEndpointTypeClusterIdAndDeviceTypeClusterId(
db,
endpointTypeClusterId,
deviceTypeClusterId
) {
let rows = await dbApi.dbAll(
db,
`
SELECT
EVENT.EVENT_ID,
EVENT.NAME,
EVENT.CLUSTER_REF,
EVENT.SIDE,
EVENT.CONFORMANCE,
COALESCE(ENDPOINT_TYPE_EVENT.INCLUDED, 0) AS INCLUDED
FROM
EVENT
JOIN
DEVICE_TYPE_CLUSTER
ON
EVENT.CLUSTER_REF = DEVICE_TYPE_CLUSTER.CLUSTER_REF
LEFT JOIN
ENDPOINT_TYPE_EVENT
ON
EVENT.EVENT_ID = ENDPOINT_TYPE_EVENT.EVENT_REF
AND
ENDPOINT_TYPE_EVENT.ENDPOINT_TYPE_CLUSTER_REF = ?
WHERE
DEVICE_TYPE_CLUSTER.DEVICE_TYPE_CLUSTER_ID = ?
`,
[endpointTypeClusterId, deviceTypeClusterId]
)
return rows.map(dbMapping.map.endpointTypeEventExtended)
}

exports.selectEventsByClusterId = selectEventsByClusterId
exports.selectAllEvents = selectAllEvents
exports.selectAllEventFields = selectAllEventFields
exports.selectEventsByEndpointTypeClusterIdAndDeviceTypeClusterId =
selectEventsByEndpointTypeClusterIdAndDeviceTypeClusterId
exports.selectEventFieldsByEventId = selectEventFieldsByEventId
exports.selectEndpointTypeEventsByEndpointTypeRefAndClusterRef =
selectEndpointTypeEventsByEndpointTypeRefAndClusterRef
Expand Down
Loading

0 comments on commit c72231e

Please sign in to comment.